SQL Server中如何将NULL值下移至下一行并交换对应值?
解决NULL与后续非NULL值交换的问题
首先,咱们来拆解下你当前遇到的问题:你写的UPDATE语句只是把NULL替换成了后面的非NULL值,但没有把被拿来替换的非NULL行的order_id置为NULL,而且也没限制“后续3条记录”的范围,这就导致了重复值——原来的非NULL值还留在原地,所以才会出现2020-06-23和2020-06-22都有s003的情况。
要实现你的需求(把NULL和后续3条内的第一个非NULL交换,且链式处理直到没有可交换的非NULL),我们需要找到交换的行对,同时更新NULL行和对应的非NULL行,甚至需要循环处理(因为交换后原来的非NULL行变成新的NULL,可能还能继续和后面的非NULL交换)。
正确的解决方案(SQL Server)
下面的代码会循环处理所有符合条件的NULL行,直到没有可交换的非NULL值为止:
WHILE EXISTS ( -- 检查是否还有需要处理的NULL行(后续3条内有非NULL) SELECT 1 FROM effective e1 WHERE e1.order_id IS NULL AND EXISTS ( SELECT 1 FROM effective e2 WHERE e2.cust_code = e1.cust_code AND e2.call_date > e1.call_date AND DATEDIFF(day, e1.call_date, e2.call_date) <= 3 -- 限制后续3天(对应3条连续记录) AND e2.order_id IS NOT NULL ) ) BEGIN -- 第一步:找到当前需要交换的行对(每个NULL对应后续3条内的第一个非NULL) WITH SwapPairs AS ( SELECT e1.call_date AS null_call_date, e1.cust_code, MIN(e2.call_date) AS non_null_call_date, -- 取最早的那个非NULL行 e2.order_id AS swap_order_id FROM effective e1 JOIN effective e2 ON e1.cust_code = e2.cust_code AND e2.call_date > e1.call_date AND DATEDIFF(day, e1.call_date, e2.call_date) <= 3 AND e2.order_id IS NOT NULL WHERE e1.order_id IS NULL GROUP BY e1.call_date, e1.cust_code, e2.order_id ) -- 第二步:把NULL行的order_id替换成目标非NULL值 UPDATE e SET order_id = sp.swap_order_id FROM effective e JOIN SwapPairs sp ON e.call_date = sp.null_call_date AND e.cust_code = sp.cust_code; -- 第三步:把原来的非NULL行设为NULL,完成交换 UPDATE e SET order_id = NULL FROM effective e JOIN SwapPairs sp ON e.call_date = sp.non_null_call_date AND e.cust_code = sp.cust_code; END
代码解释
- 循环条件:先检查是否存在还能处理的NULL行——也就是某个NULL行的后续3天内有非NULL的order_id。
- SwapPairs CTE:为每个符合条件的NULL行找到它后续3天内最早的非NULL行,确定交换的配对。
- 两次UPDATE:第一次把NULL行的order_id替换成目标值,第二次把目标非NULL行的order_id设为NULL,完成真正的交换。
- 循环执行:每次交换后,原来的非NULL行变成新的NULL,会再次进入循环检查是否还能和后面的非NULL交换,直到没有符合条件的行对为止。
执行这个代码后,就能得到你期望的结果:
call_date cust_code order_id 2020-06-20 A001 s001 2020-06-21 A001 s002 2020-06-22 A001 s003 2020-06-23 A001 s004 2020-06-24 A001 s005 2020-06-25 A001 NULL 2020-06-26 A001 NULL
内容的提问来源于stack exchange,提问作者SANDEEP SHARMA
相关产品推荐
相关产品推荐

