如何将列名转换为多行?

时间:2016-09-08 12:03:31

标签: sql postgresql pivot

我的遗留数据库包含一个包含多个boolean类型列的表。 E.g:

TABLE_1

id name           has_lights has_engine has_brakes  has_tyres can_move
1  bullock_cart   false      false      false       true      true
2  car            true       true       true        true      true
3  tank           true       true       true        false     true

我想为Table1编写一个SQL查询来获取id和name以及属性(由列名称表示)为true。

预期输出:

id name        attributes
-- ----        ----------
1 bullock_cart has_tyres
1 bullock_cart can_move
2 car          has_lights
2 car          has_engine
2 car          has_brakes
2 car          has_tyres
2 car          can_move
3 tank         has_lights
3 tank         has_engine
3 tank         has_brakes
3 tank         can_move

我写道:

SELECT id, name,
CASE
  WHEN has_lights THEN 'has_lights'
  WHEN has_engine THEN 'has_engine'
  WHEN has_brakes THEN 'has_brakes'
  WHEN has_tyres THEN 'has_tyres'
  WHEN can_move THEN 'can_move'
END
FROM TABLE1;

但这只得到Table1中每一行的第一个匹配属性(凭CASE-WHEN)。

以我想要的格式检索数据的正确方法是什么?任何输入/帮助将不胜感激?

注意:

  • 表格结构并不理想,但这是遗留系统,我们无法修改架构。
  • 嵌套查询是可以的,只要它们不会太慢 - 比如上面的示例(我理解慢速匹配的行数/列因子)。

3 个答案:

答案 0 :(得分:4)

最简单的方法是union all

select id, name, 'has_lights' as attribute from t where has_lights union all
select id, name, 'has_engine' from t where has_engine union all
select id, name, 'has_brakes' from t where has_brakes union all
select id, name, 'has_tyres' from t where has_tyres union all
select id, name, 'can_move' from t where can_move;

如果你有一个非常大的表,那么横向连接可能更有效:

select t.id, t.name, v.attribute
from t, lateral
     (select attribute
      from (values (has_lights, 'has_lights'),
                   (has_engine, 'has_engine'),
                   (has_brakes, 'has_brakes'),
                   (has_tyres, 'has_tyres'),
                   (can_move, 'can_move')
           ) v(flag, attribute)
      where flag
     ) v;

答案 1 :(得分:2)

您可以使用UNION ALL执行此操作:

SELECT name,'has_lights' as attributes FROM YourTable where has_lights = 'TRUE'
UNION ALL
SELECT name,'has_engine' as attributes FROM YourTable where has_engine= 'TRUE'
UNION ALL
SELECT name,'has_brakes' as attributes FROM YourTable where has_brakes = 'TRUE'
UNION ALL
SELECT name,'has_tyres' as attributes FROM YourTable where has_tyres = 'TRUE'
UNION ALL
SELECT name,'can_move' as attributes FROM YourTable where can_move = 'TRUE'

答案 2 :(得分:1)

这就像精彩的查询@Gordon posted

SELECT t.id, t.name, v.attribute
FROM   table1 t
JOIN   LATERAL (
   VALUES (has_lights, 'has_lights')
        , (has_engine, 'has_engine')
        , (has_brakes, 'has_brakes')
        , (has_tyres , 'has_tyres')
        , (can_move  , 'can_move')
   ) v(flag, attribute) ON v.flag;

因为VALUES表达式可以独立存在,所以会缩短一点。