如何使用 Pivot SQL

how to use Pivot SQL

例如,

select A,B,C,D,E,YEAR
FROM t1
where t1.year = 2018
UNION ALL
select A,B,C,D,E,YEAR
FROM t2
where t2.year = 2017

这样执行

A --- B----C----D----E----YEAR
2 --- 4----6----8----10---2018
1 --- 3----5----7----9----2017

我想要这样的结果

  2018  2017
A  2    1
B  4    3
C  6    5
D  8    7
E  10   9

我知道我应该使用 pivot,并四处搜索,但我不知道如何编写代码来获得如上所示的结果。

谢谢

这有点棘手 -- 取消透视和重新透视。这是一种方法:

select col,
       max(case when year = 2018 then val end) as val_2018,
       max(case when year = 2017 then val end) as val_2017
from ((select 'A' as col, A as val, YEAR from t1 where year = 2018) union all
      (select 'B' as col, B as val, YEAR from t1 where year = 2018) union all
      (select 'C' as col, C as val, YEAR from t1 where year = 2018) union all
      (select 'D' as col, D as val, YEAR from t1 where year = 2018) union all
      (select 'E' as col, D as val, YEAR from t1 where year = 2018) union all
      (select 'A' as col, A as val, YEAR from t2 where year = 2017) union all
      (select 'B' as col, B as val, YEAR from t2 where year = 2017) union all
      (select 'C' as col, C as val, YEAR from t2 where year = 2017) union all
      (select 'D' as col, D as val, YEAR from t2 where year = 2017) union all
      (select 'E' as col, D as val, YEAR from t2 where year = 2017) 
     ) tt
group by col;

您没有指定数据库,但这是完全独立于数据库的。

假设您使用的是 Oracle 11.1 或更高版本,您可以使用 pivotunpivot 运算符。在您的问题中,数据已经是 "pivoted" 一种方式,但您希望它以另一种方式旋转;所以你必须先取消旋转,然后按照你想要的方式重新旋转。在下面的解决方案中,数据是从 table 读取的(我使用 WITH 子句生成测试数据,但您不需要 WITH 子句,您可以从 SELECT 开始并使用您的实际 table 和列名)。数据通过 unpivot 提供,然后立即提供给 pivot - 您不需要子查询或类似的东西。

关于列名的注意事项:不要使用 year,它是一个 Oracle 关键字,如果不是(非常)糟糕,您会造成混淆。并且在输出中,您不能有 2018 等列名 - 标识符必须以字母开头。您可以使用双引号中的名称来绕过这些限制;不过,这是一种非常糟糕的做法,最好只留给 Oracle 解析器,而不要被我们人类使用。你会看到我调用了输入列 yr 和输出列 y2018 等等。

with
  inputs ( a, b, c, d, e, yr ) as (
    select 2, 4, 6, 8, 10, 2018 from dual union all
    select 1, 3, 5, 7,  9, 2017 from dual
  )
select   col, y2018, y2017
from     inputs
unpivot  ( val for col in (a as 'A', b as 'B', c as 'C', d as 'D', e as 'E') )
pivot    ( min(val) for yr in (2018 as y2018, 2017 as y2017) )
order by col   --   if needed
;

COL      Y2018      Y2017
--- ---------- ----------
A            2          1
B            4          3
C            6          5
D            8          7
E           10          9

添加:

这是过去的做法(在 Oracle 11.1 中引入 pivotunpivot 之前)。反透视是通过交叉连接到一个小助手 table 完成的,其中有一个列和与基础中要反透视的列一样多的行 table - 在这种情况下,五列,a, b, c, d, e 需要逆透视,所以助手 table 有五行。旋转是通过条件聚合完成的。两者都可以组合成一个查询——不需要子查询(除了创建助手 "table" 或内联视图)。

请注意,重要的是,基数 table 只被读取一次。其他反旋转方法效率低得多,因为它们需要多次读取基数 table。

select decode(lvl, 1, 'A', 2, 'B', 3, 'C', 4, 'D', 5, 'E') as col,
       max(case yr when 2018 then decode(lvl, 1, a, 2, b, 3, c, 4, d, 5, e) end) as y2018,
       max(case yr when 2017 then decode(lvl, 1, a, 2, b, 3, c, 4, d, 5, e) end) as y2017
from   inputs cross join ( select level as lvl from dual connect by level <= 5 )
group by decode(lvl, 1, 'A', 2, 'B', 3, 'C', 4, 'D', 5, 'E')
order by decode(lvl, 1, 'A', 2, 'B', 3, 'C', 4, 'D', 5, 'E')
;

这看起来比实际情况更糟;同一个 decode() 函数被调用了三次,但参数完全相同,所以它只计算一次,值被缓存并在其他地方重用。 (为 group by 计算,然后为 selectorder by 重复使用。)

要进行测试,您可以使用与上面相同的 WITH 子句 - 或者您的实际数据。

decode() 是 Oracle 专有的,但同样可以用 case 表达式编写(与 decode() 方法基本相同,只是语法不同)并且它适用于大多数其他数据库产品。