从 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 行转列,不必写出列名。

问题来源:https://stackoverflow.com/questions/78240448/convert-segmented-time-periods-in-sql-using-cross-apply-pivot