解决While循环函数中的重复数据问题:按指定键更新
解决While循环中的重复数据问题(按指定键聚合更新)
问题现状
当前通过While循环动态为临时表添加工序列并更新完成率,但执行后出现重复行,每个工序单独占一行,其他工序列值为NULL:
| ID | 销售订单 | 工单 | 客户 | 材料类型 | 零件厚度 | 需求数量 | LCT - LASER CUTTING | BNS - BENDING SIMPLE | QCT - QUALITY CONTROL |
|---|---|---|---|---|---|---|---|---|---|
| 139690 | 000001000071138 | JG00415389 | GHH002 - GHHMM MANUFACTURING (PTY) LTD | SS304L | 6.000000 | 3.000000 | 100.00 | NULL | NULL |
| 139691 | 000001000071138 | JG00415389 | GHH002 - GHHMM MANUFACTURING (PTY) LTD | SS304L | 6.000000 | 3.000000 | NULL | 100.00 | NULL |
| 139692 | 000001000071138 | JG00415389 | GHH002 - GHHMM MANUFACTURING (PTY) LTD | SS304L | 6.000000 | 3.000000 | NULL | NULL | 0.00 |
需要将同一工单的所有工序完成率合并到同一行,避免数据重复。
原代码问题分析
- 循环更新时,
ManufacturingTracking与#temp1仅通过ManufacturingTrackingId关联,若ManufacturingTracking中同一工单对应多条不同工序的记录,会导致#temp1生成重复行。 - 逐行循环的方式效率低下,且容易因关联逻辑不严谨导致数据拆分。
最优解决方案:使用动态PIVOT实现行转列
无需使用While循环,直接通过动态透视表一次性完成列生成与数据聚合,自动按指定键(如工单、销售订单)合并数据:
步骤1:准备唯一的基础数据
先确保#temp1中只有核心维度的唯一行,避免初始数据重复:
SELECT ID, 销售订单, 工单, 客户, 材料类型, 零件厚度, 需求数量 INTO #temp1 FROM 你的核心业务表 -- 替换为实际来源表 GROUP BY ID,销售订单,工单,客户,材料类型,零件厚度,需求数量;
步骤2:动态生成透视表SQL
DECLARE @WorkCentreList NVARCHAR(MAX); DECLARE @PivotSQL NVARCHAR(MAX); -- 拼接所有需要转为列的工序名称(兼容特殊字符) SELECT @WorkCentreList = STRING_AGG(QUOTENAME(WorkCentre), ',') FROM #WorkCentre2; -- 构建动态透视语句 SET @PivotSQL = N' SELECT t.ID, t.销售订单, t.工单, t.客户, t.材料类型, t.零件厚度, t.需求数量, ' + @WorkCentreList + ' FROM #temp1 t LEFT JOIN ( SELECT ManufacturingTrackingId, ' + @WorkCentreList + ' FROM ( SELECT ManufacturingTrackingId, WorkCentre, PercentCompleted FROM ManufacturingTracking (NOLOCK) ) src PIVOT ( MAX(PercentCompleted) -- 若同一工序有多条记录,用MAX/AVG等聚合函数取对应值 FOR WorkCentre IN (' + @WorkCentreList + ') ) pvt ) p ON t.ID = p.ManufacturingTrackingId;'; -- 执行动态SQL EXEC sp_executesql @PivotSQL;
代码说明
STRING_AGG替代循环逻辑,自动拼接所有工序为合法列名。- PIVOT子句将
ManufacturingTracking中的行数据(不同工序完成率)转为列数据,自动按ManufacturingTrackingId聚合,彻底避免重复行。 LEFT JOIN确保核心数据行不会因工序缺失而丢失,对应列显示NULL。
替代方案:修复原有循环逻辑(不推荐,效率低)
若必须保留循环方式,需先确保#temp1行唯一,再执行更新:
-- 第一步:去重#temp1,保留唯一核心行 WITH CTE AS ( SELECT *, ROW_NUMBER() OVER(PARTITION BY 工单,销售订单 ORDER BY ID) AS rn FROM #temp1 ) DELETE FROM CTE WHERE rn > 1; -- 第二步:执行循环更新(此时仅匹配唯一行,不会生成重复) Declare @loop1 int = 0 Declare @loop2 int = (Select count(*) from #WorkCentre2) Declare @SQL varchar(8000) Declare @WorkCentre VARCHAR(50) while @loop1 < @loop2 begin Set @loop1 = @loop1 + 1 SELECT @WorkCentre = WorkCentre from #WorkCentre2 where AutoID = @loop1; Set @SQL = 'Alter table #temp1 add [' + @WorkCentre + '] varchar(50)' exec(@SQL) Set @SQL = ' Update #temp1 set [' + @WorkCentre + '] = mt.PercentCompleted From ManufacturingTracking mt (nolock) inner join #temp1 t on t.ID = mt.ManufacturingTrackingId and mt.WorkCentre = ''' + @WorkCentre + '''' exec(@SQL) end
内容的提问来源于stack exchange,提问作者Anton
相关产品推荐
相关产品推荐

