从 SQL 到 SPL:交叉格转成行头,行头转成列
SQL Server 库某表可以看成是交叉表,前 2 个字段值的组合可以看成行头,后 3 个字段名可以看成列头,其中行头的内容和数量不确定。
ID |
PartNum |
EnteredOn |
PickedTime |
DeliveredTime |
100 |
50A |
2024-03-28 08:59:13.727 |
2024-03-28 09:30:20.237 |
2024-03-28 09:56:42.570 |
125 |
60B |
2024-03-28 08:59:22.290 |
2024-03-28 09:31:32.543 |
2024-03-28 09:56:50.683 |
171 |
50A |
2024-03-28 14:31:28.480 |
||
211 |
70B |
2024-03-28 14:31:33.613 |
现在要把该表转换成新的交叉表,原交叉格转成新行头,原行头转成列头,原列头 EnteredOn、PickedTime、DeliveredTime 分别替换成字符串 ENTERED、PICKED、DELIVERED。
t_stamp |
50A_100 |
60B_125 |
50A_171 |
70B_211 |
2024-03-28 08:59:13.727 |
ENTERED |
|||
2024-03-28 08:59:22.290 |
ENTERED |
|||
2024-03-28 09:30:20.237 |
PICKED |
|||
2024-03-28 09:31:32.543 |
PICKED |
|||
2024-03-28 09:56:42.570 |
DELIVERED |
|||
2024-03-28 09:56:50.683 |
DELIVERED |
|||
2024-03-28 14:31:28.480 |
ENTERED |
|||
2024-03-28 14:31:33.613 |
ENTERED |
动态 SQL:
Declare @SQL varchar(max) = (
Select string_agg(col,',')
From (Select distinct id,Col = quotename(concat(PartNum,'_',ID))
From YourTable
) A
)
Set @SQL = '
Select *
From (
Select Item = concat(PartNum,''_'',ID)
,B.*
From YourTable A
CROSS APPLY (VALUES (EnteredOn,''ENTERED'')
,(PickedTime,''PICKED'')
,(DeliveredTime,''DELIVERED'')
) B(t_stamp,[Status])
) src
Pivot ( max(Status) for Item in ('+ @SQL +') ) pvt
Where t_stamp is not null
Exec(@SQL)
普通 SQL 有 pivot 函数进行行列转换,但必须写出列名,这就要改变代码结构,先用存储过程或动态 SQL 生成列名,再拼出 SQL,代码非常复杂。
SPL 代码要简单易懂很多:
A |
|
1 |
=mssql.query("select concat(PartNum,'_',ID) as PID, EnteredOn, PickedTime, DeliveredTime from tb") |
2 |
=A1.pivot@r(PID;x,t_stamp;EnteredOn:"ENTERED",PickedTime:"PICKED",DeliveredTime:"DELIVERED") |
3 |
=A2.select(t_stamp) |
4 |
=A3.pivot(t_stamp;PID,x) |
A1:加载数据,将前 2 个字段的值拼起来。
A2、A4:用 pivot@r 列转行,用 pivot 行转列,不必写出列名。