从 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 代码要简单易懂很多: https://try.esproc.com/splx?3CA
A | |
1 | $select concat(PartNum,'_',ID) as PID, EnteredOn, PickedTime, DeliveredTime from tb.txt |
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 行转列,不必写出列名。
英文版 https://c.esproc.com/article/1737506411372