MySQL - How to unpivot columns to rows?

前端 未结 3 2110
没有蜡笔的小新
没有蜡笔的小新 2020-11-22 08:58

I\'m probably not seeing things very clear at this moment, but I have a table in MySQL which looks like this:

ID | a  | b  | c 
1  | a1 | b1 | c1
2  | a2 | b         


        
相关标签:
3条回答
  • 2020-11-22 09:22

    Try to use UNION ALL.

    SELECT ID, a, 'a' 
    FROM tbl
    WHERE ID = 1
    UNION
    SELECT ID, b, 'b' 
    FROM tbl
    WHERE ID = 2
    
    0 讨论(0)
  • 2020-11-22 09:29

    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

    0 讨论(0)
  • 2020-11-22 09:31

    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   
    

    Side note

    MySQL 8.0.19 added 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).

    nfortunately, As of version 8.0.21, this does not work yet - which might be considered a bug - but maybe will in a future version...:

    select t.id, x.*
    from mytable t
    cross join lateral (values 
        row(a, 'a'), 
        row(b, 'b'),
        row(c, 'c')
    ) as x(col1, col2);
    
    0 讨论(0)
提交回复
热议问题