MySQL - 将表转换为不同的表

时间:2013-03-03 09:40:59

标签: mysql unpivot

此刻我可能没有看清楚事情,但我在MySQL中有一个表格如下:

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

由于某种原因(实际上是另一个表的连接 - 基于ID,但我想如果有人可以帮我解决这个问题,我可以自己做其余的事情),我需要那些行就像相反:

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

基本上,我需要查看以下行:IDcolumntitlevalue 有没有办法轻松做到这一点?

3 个答案:

答案 0 :(得分:20)

您正在尝试取消数据。 MySQL没有unpivot函数,因此您必须使用UNION ALL查询将列转换为行:

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

请参阅SQL Fiddle with Demo

这也可以使用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

请参阅SQL Fiddle with Demo

答案 1 :(得分:3)

尝试使用UNION ALL

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

答案 2 :(得分:0)

这花了很长时间,但是MySQL 8.0.14版最终增加了对横向连接的支持-官方术语是lateral derived tables

这是一项非常强大的功能,在多种情况下都非常方便,包括将表中的列移到行中。

您可以按如下方式查询查询:

function waitBeforeAttempt() {
  const start = new Date();
  let now;
  while (true) {
    now = new Date();
    if (now - start >= 15000) {
      break;
    }
  }
}

与典型的规范解决方案相比,这似乎没有太大区别-毕竟,我们仍在横向派生表中使用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) ……但请不要误解:此查询仅扫描表一次,而另一种方法则需要对每一列进行一次扫描才能取消透视。因此,这种方法效率更高-随着表格变大和/或需要取消旋转更多列,性能提升将大大提高。

底线:如果您正在运行MySQL 8.0.14或更高版本,请使用此技术。从该版本开始,这是在MYSQL中取消透视的规范方法。

Demo on DB Fiddle

样本数据:

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

查询结果:

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

旁注

MySQL 8.0.19添加了对the VALUES statement的支持,这可以通过消除在子查询中使用union all的需要来帮助进一步缩短查询(尽管我在这里看不到任何性能提升,但这使查询更整洁。)

不幸的是,从8.0.21版开始,此无效-可能被认为是错误-但在将来的版本中可能会...:

union all