SQL Server中若前列为NULL,能否将列数据向左移动填充空列?
在SQL Server中实现非NULL值左移填充
当然可以实现!针对你这种需要把保单类型列里的非NULL值向左移动、消除左侧NULL的场景,在SQL Server里有几种高效的方案,我结合你的示例数据来详细说明:
先看你的示例数据
首先我把你的测试数据整理成可执行的SQL脚本,方便验证:
CREATE TABLE #TempPolicy ( Pol_Ref VARCHAR(20), Pol_Mix VARCHAR(20), Pol_1_Type VARCHAR(10), Pol_2_Type VARCHAR(10), Pol_3_Type VARCHAR(10), Pol_4_Type VARCHAR(10) ); INSERT INTO #TempPolicy VALUES ('XXXXXXSW01', 'Car', NULL, 'PC', NULL, NULL), ('XXXXXXSW02', 'Modern', NULL, 'PC', 'MB', NULL), ('XXXXXXSW01', 'Car', NULL, NULL, 'PC', NULL), ('XXXXXXSW03', 'Modern', 'PC', NULL, 'MB', NULL);
方法一:UNPIVOT + PIVOT(推荐,高效处理大数据)
这个方法通过列转行过滤掉NULL,再行转列重新排序填充,适合处理数万行的数据集:
WITH UnpivotedData AS ( SELECT Pol_Ref, Pol_Mix, PolicyType, -- 按原列顺序给非NULL值分配新的序号 ROW_NUMBER() OVER (PARTITION BY Pol_Ref, Pol_Mix ORDER BY Seq) AS NewColumnSeq FROM #TempPolicy -- 将4个类型列转成多行,每行对应一个类型值 UNPIVOT ( PolicyType FOR Seq IN (Pol_1_Type, Pol_2_Type, Pol_3_Type, Pol_4_Type) ) AS UnpivotStep WHERE PolicyType IS NOT NULL -- 过滤掉NULL值 ) SELECT Pol_Ref, Pol_Mix, -- 按新序号重新分配到Pol_1到Pol_4列 MAX(CASE WHEN NewColumnSeq = 1 THEN PolicyType END) AS Pol_1_Type, MAX(CASE WHEN NewColumnSeq = 2 THEN PolicyType END) AS Pol_2_Type, MAX(CASE WHEN NewColumnSeq = 3 THEN PolicyType END) AS Pol_3_Type, MAX(CASE WHEN NewColumnSeq = 4 THEN PolicyType END) AS Pol_4_Type FROM UnpivotedData GROUP BY Pol_Ref, Pol_Mix ORDER BY Pol_Ref;
执行后你会得到想要的结果:所有非NULL的保单类型都会向左填充,Pol_1_Type再也不会出现NULL。
方法二:STRING_AGG + STRING_SPLIT(适合SQL Server 2017+)
如果你的SQL Server版本是2017及以上,可以用字符串聚合拆分的方式实现,逻辑更直观:
WITH AggregatedTypes AS ( SELECT Pol_Ref, Pol_Mix, -- 把每个分组的非NULL类型按原顺序拼接成字符串 STRING_AGG(PolicyType, ',') WITHIN GROUP (ORDER BY Seq) AS PolicyTypeList FROM #TempPolicy UNPIVOT ( PolicyType FOR Seq IN (Pol_1_Type, Pol_2_Type, Pol_3_Type, Pol_4_Type) ) AS UnpivotStep WHERE PolicyType IS NOT NULL GROUP BY Pol_Ref, Pol_Mix ), SplitTypes AS ( SELECT Pol_Ref, Pol_Mix, value AS PolicyType, -- 给拆分后的类型分配序号 ROW_NUMBER() OVER (PARTITION BY Pol_Ref, Pol_Mix ORDER BY (SELECT NULL)) AS NewColumnSeq FROM AggregatedTypes -- 把拼接的字符串拆分成多行 CROSS APPLY STRING_SPLIT(PolicyTypeList, ',') ) SELECT Pol_Ref, Pol_Mix, MAX(CASE WHEN NewColumnSeq = 1 THEN PolicyType END) AS Pol_1_Type, MAX(CASE WHEN NewColumnSeq = 2 THEN PolicyType END) AS Pol_2_Type, MAX(CASE WHEN NewColumnSeq = 3 THEN PolicyType END) AS Pol_3_Type, MAX(CASE WHEN NewColumnSeq = 4 THEN PolicyType END) AS Pol_4_Type FROM SplitTypes GROUP BY Pol_Ref, Pol_Mix ORDER BY Pol_Ref;
注意事项
- 两种方法都能高效处理数万行数据,其中UNPIVOT/PIVOT的性能更优,因为避免了字符串处理的额外开销;
- 如果你的实际表中还有其他需要保留的列,只需要在CTE和
GROUP BY子句中加入对应的列即可; - 确保
ORDER BY Seq的顺序和原列的顺序一致(Pol_1_Type在前,Pol_4_Type在后),这样左移后的顺序才符合预期。
内容的提问来源于stack exchange,提问作者Phteven
相关产品推荐
相关产品推荐

