循环按活动随机删除表行脚本仅首次生效问题排查
你的脚本只对第一个Campaign生效,后续没删除效果,核心问题是CTE里的ROW_NUMBER分区逻辑错误,跟随机数生成方式没关系,不用改成函数。
问题出在哪
行号是全局的,没按Campaign单独排:
你写的PARTITION BY 1会给整个表的所有行生成连续的全局行号,不是每个Campaign内部独立的行号。比如第一个Campaign删完后,后续Campaign的行的全局行号可能重新从很小的数字开始,要是随机生成的@AttendeesDeleted比这个数字大,rn > @AttendeesDeleted就找不到匹配的行,自然删不了。根本不是随机删除:
你用ORDER BY CampaignAttendeeID排行号,删的是ID大的那些行,这是按固定顺序删,不是随机删,不符合你要随机删若干行的需求。随机数可能超过Campaign的总人数:
要是@AttendeesDeleted比当前Campaign的总人数还大,那rn > @AttendeesDeleted根本没对应的行,也删不了。
改好的脚本
DECLARE @CampaignID INT = 1 WHILE @CampaignID <= 1000 BEGIN -- 先查当前Campaign有多少人,避免随机数瞎生成 DECLARE @TotalAttendees INT SELECT @TotalAttendees = COUNT(*) FROM Warehouse.CampaignAttendees WHERE CampaignID = @CampaignID -- 没人就直接跳过 IF @TotalAttendees = 0 BEGIN PRINT 'Campaign ' + CAST(@CampaignID AS VARCHAR) + ' 没参会人,跳过' SET @CampaignID = @CampaignID + 1 CONTINUE END -- 生成0到总人数之间的随机数,确定要保留多少人(删的就是总人数减保留数) DECLARE @AttendeesToKeep INT = ROUND(RAND() * @TotalAttendees, 0) ;WITH attendees AS ( SELECT *, -- 按Campaign分区,用NEWID()随机排,生成每个Campaign内部的随机行号 ROW_NUMBER() OVER (PARTITION BY CampaignID ORDER BY NEWID()) AS rn FROM Warehouse.CampaignAttendees WHERE CampaignID = @CampaignID -- 先过滤当前Campaign,跑起来更快 ) DELETE FROM attendees WHERE rn > @AttendeesToKeep -- 删掉随机排序后行号超过保留数的行,就是随机删人 PRINT 'Campaign ' + CAST(@CampaignID AS VARCHAR) + ': 删掉了 ' + CAST(@TotalAttendees - @AttendeesToKeep AS VARCHAR) + ' 个参会人' SET @CampaignID = @CampaignID + 1 END
关键调整
- 按Campaign单独排行号:
PARTITION BY CampaignID让每个Campaign的行号从1开始算,不会再出现全局行号导致的匹配问题。 - 真·随机删除:用
ORDER BY NEWID()让行随机排序,删的是随机选出来的行,符合你的需求。 - 适配实际人数:先查当前Campaign的总人数,随机数限定在合理范围内,不会再出现随机数太大没行可删的情况。
- 性能优化:CTE里先过滤当前Campaign的数据,不用扫全表,速度更快。
内容的提问来源于stack exchange,提问作者William Gitu
相关产品推荐
相关产品推荐

