MySQL Query to Dynamically Convert Rows to Columns

Mysql query to dynamically convert rows to columns on the basis of two columns

If you had a known number of values for both order and item, then you could hard code the query into:

select id,
max(case when `order` = 1 then data end) order1,
max(case when `order` = 2 then data end) order2,
max(case when `order` = 3 then data end) order3,
max(case when item = 1 then price end) item1,
max(case when item = 2 then price end) item2,
max(case when item = 3 then price end) item3,
max(case when item = 4 then price end) item4
from tableA
group by id;

See Demo. But part of the problem that you are going to have is because you are trying to transform multiple columns of data. My suggestion to get the final result would be to unpivot the data first. MySQL does not have an unpivot function but you can use a UNION ALL to convert the multiple pairs of columns into rows. The code to unpivot will be similar to the following:

select id, concat('order', `order`) col,  data value
from tableA
union all
select id, concat('item', item) col, price value
from tableA;

See Demo. The result of this will be:

| ID |    COL | VALUE |
-----------------------
| 1 | order1 | P |
| 1 | order1 | P |
| 1 | order1 | P |
| 1 | item1 | 50 |
| 1 | item2 | 60 |
| 1 | item3 | 70 |

As you can see this has taken the multiple columns of order/data and item/price and convert it into multiple rows. Once that is completed, then you can convert the values back into columns using an aggregate function with a CASE:

select id, 
max(case when col = 'order1' then value end) order1,
max(case when col = 'order2' then value end) order2,
max(case when col = 'order3' then value end) order3,
max(case when col = 'item1' then value end) item1,
max(case when col = 'item2' then value end) item2,
max(case when col = 'item3' then value end) item3
from
(
select id, concat('order', `order`) col, data value
from tableA
union all
select id, concat('item', item) col, price value
from tableA
) d
group by id;

See Demo. Finally, you need to convert the above code into a dynamic prepared statement query:

SET @sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'max(case when col = ''',
col,
''' then value end) as `',
col, '`')
) INTO @sql
FROM
(
select concat('order', `order`) col
from tableA
union all
select concat('item', `item`) col
from tableA
)d;

SET @sql = CONCAT('SELECT id, ', @sql, '
from
(
select id, concat(''order'', `order`) col, data value
from tableA
union all
select id, concat(''item'', item) col, price value
from tableA
) d
group by id');

PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

See SQL Fiddle with demo. This gives a result:

| ID | ORDER1 | ORDER2 | ORDER3 | ITEM1 | ITEM2 |  ITEM3 |  ITEM4 |
-------------------------------------------------------------------
| 1 | P | Q | (null) | 50 | 60 | 70 | (null) |
| 2 | P | (null) | S | 50 | 60 | (null) | 80 |

Convert Rows to Columns in MySQL Dynamically

You can't have dynamic columns, at least without dynamically generating the SQL. You can build the SQL in a stored procedure as per this answer

MySQL pivot table query with dynamic columns

Alternatively it might be simpler to do this in your application code by selecting the distinct subjects in one query and using that result set to build the SQL that retrieves the result set you are after. At least with the logic in the application code you have some idea of how many columns you will be seeing in the result set.

Mysql query to dynamically convert rows to columns

You can use GROUP BY and MAX to simulate pivot. MySQL also supports IF statement.

SELECT  ID,
MAX(IF(`order` = 1, data, NULL)) data1,
MAX(IF(`order` = 2, data, NULL)) data2
FROM TableA
GROUP BY ID
  • SQLFiddle Demo

If you have multiple values of order, dynamic SQL may be more appropriate so that you will not have to modify the query:

SET @sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'MAX(IF(`order` = ', `order`, ',data,NULL)) AS data', `order`)
) INTO @sql
FROM TableName;

SET @sql = CONCAT('SELECT ID, ', @sql, '
FROM TableName
GROUP BY ID');

PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
  • SQLFiddle Demo
  • SQLFiddle Demo (Another example)

OUTPUT OF BOTH QUERIES:

╔════╦═══════╦═══════╗
║ ID ║ DATA1 ║ DATA2 ║
╠════╬═══════╬═══════╣
║ 1 ║ P ║ S ║
║ 2 ║ R ║ Q ║
╚════╩═══════╩═══════╝

How to dynamically convert row to column in MySQL and display the results in Swing

Creating a Stored Procedure with a Prepared Statement

First, you must create a stored procedure using a prepared statement. I have not tested your code, but the stored procedure can be created using code like the following.

DROP PROCEDURE IF EXISTS execSql;
DELIMITER //
CREATE PROCEDURE execSql ()
BEGIN

SET @sql = CONCAT( ...

PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END //
DELIMITER ;

Invoking a MySQL Stored Procedure using JDBC

JDBC offers a CallableStatement class you can use to invoke stored procedures. You may check a tutorial for Calling MySQL Stored Procedures from JDBC.

// use CALL with the name of the stored procedure
String query = "{CALL execSql()}";

// use CallableStatement to obtain the ResultSet
CallableStatement stmt = conn.prepareCall(query)
ResultSet rs = stmt.executeQuery();

Showing the result in a Swing JTable

To display the data, you may create a TableModel with the ResultSet. There are many classes/libraries that you can use to fill a TableModel with the resultset (such as this DbUtils or this JDBCTableModel).

// execute the query
String query = "{CALL execSql()}";
CallableStatement stmt = conn.prepareCall(query)
ResultSet rs = stmt.executeQuery();

// fill the TableModel with the results
TableModel normalTableModel = DBUtils.resultSetToTableModel(rs);

// create the Swing Table
JTable swingTable = new JTable (transposedTableModel);

Transposing a TableModel

Sometimes you want to transpose (convert rows to columns) the results ot a Query. Although MySQL does not support PIVOT/UNPIVOT, you can transpose the Swing's TableModel. I think transposing the TableModel is the better (simpler) alternative.

Basically, you can create a TableModel wrapper that transpose the results. You may check the Spring TableModel and the JideSoft TransposeTableModel. The code for a TransposedTableModel may be something like the following...

public TransposedTableModel extends AbstractTableModel {

private TableModel innerTableModel;

public TransposedTableModel(TableModel innerTableModel) {
this.innerTableModel = innerTableModel;
}

@Override
public int getRowCount() {
return innerTableModel.getColumnCount();
}

@Override
public int getColumnCount() {
return innerTableModel.getRowCount();
}

Override
public Object getValue(int row, int column) {
return innerTableModel.getValue(column, row);
}
};

Then, you can use a "normal" SQL, fill a TableModel with the result and the use the transposed TableModel.

stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM table1");

TableModel normalTableModel = DBUtils.resultSetToTableModel(rs);
TableModel transposedTableModel = new TransposedTableModel(normalTableModel);

JTable swingTable = new JTable (transposedTableModel);

Mysql, Dynamically Convert Rows to Columns

looks like you've ran into maximum length of GROUP_CONCAT which is 1024 characters long.
you can use this SET SESSION group_concat_max_len to increase its length

SET SESSION group_concat_max_len = 1000000;
SET @sql = NULL;
SELECT GROUP_CONCAT(DISTINCT CONCAT( 'MAX(CASE WHEN period = ', period, ' then value ELSE NULL end) AS period', period, '') ) INTO @sql FROM items;
SET @sql = CONCAT('SELECT prod,`group`,item_code,prop_code, ', @sql, ' FROM items GROUP BY prod,`group`,item_code,prop_code');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

sqlfiddle

mysql query to dynamically convert row data to columns

You simply cannot have a static SQL statement returning a variable number of columns. You need to build such statement each time the number of different districts changes. To do that, you execute first a

SELECT DISTINCT District FROM district_details;

This will give you the list of districts where there are details. You then build a SQL statement iterating over the previous result (pseudocode)

statement = "SELECT name "

For each row returned in d = SELECT DISTINCT District FROM district_details
statement = statement & ", SUM(IF(District=""" & d.District & """,1 ,0)) AS """ & d.District & """"

statement = statement & " FROM district_details GROUP BY name;"

And execute that query. You'll then need have to handle in your code the processing of the variable number of columns

MYSQL convert row to column

This is only slightly more complicated than a regular pivot because you need a condition on two columns:

select student_name,
max(case when assessment_type = 'QUIZ' and assesment_no = 1 then marks end) as quiz1,
max(case when assessment_type = 'QUIZ' and assesment_no = 2 then marks end) as quiz2
from student_marks
group by student_name;

You can expand this into dynamic SQL as:

SET @condition = 'MAX(CASE WHEN assessment_type = ''@at'' AND assement_no = @an THEN assessment_marks END) as @at@an';

SELECT @conditions := GROUP_CONCAT(REPLACE(REPLACE(@condition, '@at', assessment_type), '@an', assessment_no))
FROM studentmarks;

SET @sql = CONCAT('
SELECT student_name, ', @conditions, '
FROM studentmarks
GROUP BY student_name');

PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;


Related Topics



Leave a reply



Submit