30

I have a table like this

id month col1 col2 col3 col4
101 Jan A B NULL B
102 feb C A G E

And then I want to create report like this

desc jan feb
col1 A C
col2 B A
col3 0 G
Col4 B E

Can anyone help with this?

Anonymous
  • 835
  • 1
  • 5
  • 21
user1914516
  • 339
  • 1
  • 3
  • 3
  • Welcome to stackoverflow. [This is a very common question](http://stackoverflow.com/search?q=[mysql]+pivot). Please take a few minutes to search the archives. Try adapting one of the previous answers first. Then if you run into problems, post your query and any errors here. – Leigh Dec 19 '12 at 04:24
  • possible duplicate of [MySQL pivot row into dynamic number of columns](http://stackoverflow.com/questions/12004603/mysql-pivot-row-into-dynamic-number-of-columns) – RichardTheKiwi May 03 '13 at 09:03

3 Answers3

62

What you need to do is first, unpivot the data and then pivot it. But unfortunately MySQL does not have these functions so you will need to replicate them using a UNION ALL query for the unpivot and an aggregate function with a CASE for the pivot.

The unpivot or UNION ALL piece takes the data from your col1, col2, etc and turns it into multiple rows:

select id, month, col1 value, 'col1' descrip
from yourtable
union all
select id, month, col2 value, 'col2' descrip
from yourtable
union all
select id, month, col3 value, 'col3' descrip
from yourtable
union all
select id, month, col4 value, 'col4' descrip
from yourtable

See SQL Fiddle with Demo.

Result:

|  ID | MONTH |  VALUE | DESCRIP |
----------------------------------
| 101 |   Jan |      A |    col1 |
| 102 |   feb |      C |    col1 |
| 101 |   Jan |      B |    col2 |
| 102 |   feb |      A |    col2 |
| 101 |   Jan | (null) |    col3 |
| 102 |   feb |      G |    col3 |
| 101 |   Jan |      B |    col4 |
| 102 |   feb |      E |    col4 |

You then wrap this in a subquery to apply the aggregate and the CASE to convert this into the format you want:

select descrip, 
  max(case when month = 'jan' then value else 0 end) jan,
  max(case when month = 'feb' then value else 0 end) feb
from
(
  select id, month, col1 value, 'col1' descrip
  from yourtable
  union all
  select id, month, col2 value, 'col2' descrip
  from yourtable
  union all
  select id, month, col3 value, 'col3' descrip
  from yourtable
  union all
  select id, month, col4 value, 'col4' descrip
  from yourtable
) src
group by descrip

See SQL Fiddle with demo

The result is:

| DESCRIP | JAN | FEB |
-----------------------
|    col1 |   A |   C |
|    col2 |   B |   A |
|    col3 |   0 |   G |
|    col4 |   B |   E |
Taryn
  • 242,637
  • 56
  • 362
  • 405
5

Although this question is suuuper old and someone marked it as "very common", people still seem to find it (me included) and find it helpfull. I developed a more generalized version for unpivoting a row and thought it might be helpful to someone.

SET @target_schema='schema';
SET @target_table='table';
SET @target_where='`id`=1';
SELECT
    GROUP_CONCAT(qry SEPARATOR ' UNION ALL ')
    INTO @sql
FROM (
    SELECT
        CONCAT('SELECT `id`,', QUOTE(COLUMN_NAME), ' AS `key`,`', COLUMN_NAME, '` AS `value` FROM `', @target_table, '` WHERE ', @target_where) qry
    FROM (
        SELECT `COLUMN_NAME` 
        FROM `INFORMATION_SCHEMA`.`COLUMNS` 
        WHERE `TABLE_SCHEMA`=@target_schema 
            AND `TABLE_NAME`=@target_table
    ) AS `A`
) AS `B`;
PREPARE s FROM @sql; EXECUTE s; DEALLOCATE PREPARE s;

I use this query on a MySQL 8.x server and aggregate it to a JSON object there, hence the id, key, value result structure.

belowtoxic
  • 51
  • 1
  • 2
0

(Extending this great previous answer, as editing or commenting is not possible for my account at the moment)

Although this approach is not as straightforward as the UNION ALL / CASE - approach before, its advantage lies in that it can be used ("dynamically") for any number of original columns [please correct me on "any"].

A limitation that might lead to unclear errors is

the group_concat_max_len system variable, which has a default value of 1024

In that case, just try something like

SET SESSION group_concat_max_len = 92160;
Haris
  • 3
  • 3
  • This does not provide an answer to the question. Once you have sufficient [reputation](https://stackoverflow.com/help/whats-reputation) you will be able to [comment on any post](https://stackoverflow.com/help/privileges/comment); instead, [provide answers that don't require clarification from the asker](https://meta.stackexchange.com/questions/214173/why-do-i-need-50-reputation-to-comment-what-can-i-do-instead). - [From Review](/review/late-answers/32354090) – Pedro Paiva Aug 02 '22 at 22:22