从 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://stackoverflow.com/questions/78240448/convert-segmented-time-periods-in-sql-using-cross-apply-pivot