SQL 如何将每组多列数据转到同一行 *
有数据库表STAKEHOLDER,数据如下所示:
CLASS
NAME
SID
1
name1
001
1
name2
002
2
name3
003
2
name4
004
2
name5
005
需要按CLASS 分组,并将其余列转到同一行,结果如下所示:
CLASS
NAME1
SID1
NAME2
SID2
NAME3
SID3
1
name1
001
name2
002
2
name3
003
name4
004
name5
005
Oracle的SQL:
WITH CTE AS(
SELECT
UP.CLASS,
UP.NS || UP.RN AS NSR,
UP.VAL
FROM
(
SELECT
ROW_NUMBER ()
OVER (
PARTITION BY S.CLASS
ORDER BY
S.CLASS) RN,
S.*
FROM
STAKEHOLDER S
ORDER BY
CLASS,
SID) SS
UNPIVOT (VAL FOR NS IN (NAME, SID)) UP
)
SELECT
*
FROM
CTE
PIVOT(MAX(VAL) FOR NSR IN ('NAME1' AS NAME1,
'SID1' AS SID1,
'NAME2' AS NAME2,
'SID2' AS SID2,
'NAME3' AS NAME3,
'SID3' AS SID3))
用自然思维去解这道题并不难,按CLASS 分组后,只需要将 NAME 与 SID 这两列先转成行,同时建立需要转列的值所在列名,这里利用原列名 + 组内序号,例如 NAME1,SID1,NAME2,SID2,…,下一个组内又是NAME1,SID1,…,再合并各组数据后,行转列即可。SQL 不支持动态行列转换,当列数较少且固定时,勉强可以应付。但是当列数较多时,因为转换过程中需要枚举出所有列,所以写起来很费劲,写出的 SQL 也非常臃肿。如果是动态列,那就需要更复杂的办法来解了。
用开源集算器的SPL 就很容易写:
A |
|
1 |
=connect("ORACLE") |
2 |
=A1.query@x("SELECT * FROM STAKEHOLDER ORDER BY CLASS,SID") |
3 |
=A2.fname().m(2:) |
4 |
=A2.group@o(CLASS) |
5 |
=A4.conj(~.news(A3;CLASS,A3(#)/A4.~.#:COL,eval(~):VAL)) |
6 |
=A5.pivot(CLASS;COL,VAL) |
SPL 是专业的数据计算引擎,基于有序集合设计,提供了完善的集合运算,支持分步计算,更切合自然思维。相比 SQL 需要枚举列名,SPL 可以直接自动扩展,很容易处理各种行列转换问题。
英文版