25
ID | a  | b  | c 
1  | a1 | b1 | c1
2  | a2 | b2 | c2

How do I reorganize the rows as ID, columntitle, value?

1 | a1 | a
1 | b1 | b
1 | c1 | c
2 | a2 | a
2 | b2 | b
2 | c2 | c
2
  • are you mean 'ID,value,columntitle' ? Commented Mar 3, 2013 at 9:44
  • Yes, but the order doesn't matter so much, as long as the rows are ID - column title - value Commented Mar 3, 2013 at 19:31

3 Answers 3

31

You are trying to unpivot the data. MySQL does not have an unpivot function, so you will have to use a UNION ALL query to convert the columns into rows:

select id, 'a' col, a value
from yourtable
union all
select id, 'b' col, b value
from yourtable
union all
select id, 'c' col, c value
from yourtable

See SQL Fiddle with Demo.

This can also be done using a CROSS JOIN:

select t.id,
  c.col,
  case c.col
    when 'a' then a
    when 'b' then b
    when 'c' then c
  end as data
from yourtable t
cross join
(
  select 'a' as col
  union all select 'b'
  union all select 'c'
) c

See SQL Fiddle with Demo

Sign up to request clarification or add additional context in comments.

Comments

8

It took a long time coming, but MySQL version 8.0.14 finally added support for lateral joins - the official terminology is lateral derived tables.

This is a very powerful feature, that comes handy in multiple situations, including unpivoting table columns to rows.

You can phrase the query as follows:

select t.id, x.*
from mytable t
cross join lateral (
    select a, 'a' 
    union all select b, 'b'
    union all select c, 'c'
) as x(col1, col2)

It may look like this is not a big difference compared to the typical cannonical solution - after all, we are still using union all within the lateral derived table... But don't get it wrong: this query scans the table only once, as opposed to the other approach, which requires one scan for each column to unpivot. So this is more efficient - and the performance gain increases dramatically as the table goes bigger and/or more columns need to be unpivoted.

Bottom line: if you are running MySQL 8.0.14 or higher, just use this technique. From that version onwards, this is the canonical way to unpivot in MYSQL.

Demo on DB Fiddle:

Sample data:

ID | a  | b  | c 
-: | :- | :- | :-
 1 | a1 | b1 | c1
 2 | a2 | b2 | c2

Query results:

id | col1 | col2
-: | :--- | :---
 1 | a1   | a   
 1 | b1   | b   
 1 | c1   | c   
 2 | a2   | a   
 2 | b2   | b   
 2 | c2   | c   

Update

MySQL 8.0.19 first introduced support for the VALUES statement, which could help further shortening the query by removing the need to use union all in a subquery (although I don't see any performance gain here, this makes the query neater). As of version 8.0.30, we can express the lateral join like so:

select t.id, x.*
from mytable t
cross join lateral (values 
    row(a, 'a'), 
    row(b, 'b'),
    row(c, 'c')
) as x(col1, col2);

1 Comment

There appears to be a bug when using values statement with lateral derived tables. The values statement is only outputting data for the first row of the outer table.
3

Try to use UNION ALL.

SELECT ID, a, 'a' 
FROM tbl
WHERE ID = 1
UNION
SELECT ID, b, 'b' 
FROM tbl
WHERE ID = 2

1 Comment

Get rid of the WHERE clauses. Otherwise you're only showing one column for each ID, instead of all columns for each ID.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.