在 SQL 的集合思维世界里,我们总说要避免“逐行处理”。但有时候,你确实需要一个接一个地处理某些事情。比如:批量更新数据、逐行调用存储过程、或者执行一些需要重复的逻辑。
这时候,WHILE 循环就登场了。它是 SQL 里最基础的流程控制语句,能让你重复执行一段代码块,直到某个条件不再满足。
一、WHILE循环长什么样
先看看它的基本结构:
DECLARE @counter INT = 1; -- 初始化计数器
WHILE @counter <= 5 -- 循环条件:当计数器小于等于5时
BEGIN
PRINT '这是第 ' + CAST(@counter AS VARCHAR) + ' 次循环';
SET @counter = @counter + 1; -- 千万不要忘了改变条件,否则...
END
PRINT '循环结束!';
运行结果:
这是第 1 次循环
这是第 2 次循环
这是第 3 次循环
这是第 4 次循环
这是第 5 次循环
循环结束!
关键点:
WHILE (条件):条件为真就继续循环,为假就退出。BEGIN...END:包裹要重复执行的代码块(如果只有一行,可以省略,但强烈建议总是加上)。- 必须要有改变循环条件的语句,否则就是无限循环(我们稍后会说这个)。
二、数据批处理
这是 WHILE 循环最常见、最实用的场景。假设你要更新一个百万级别的表,直接一个 UPDATE 可能锁表太久,影响生产环境。这时可以分批处理。
场景:给所有员工涨薪5%,但每次只更新1000条
-- 假设我们有 Employees 表
CREATETABLE#Employees (
EmployeeID INT PRIMARY KEY,
Salary DECIMAL(10,2)
);
-- 插入10万条测试数据(实际中你的表可能已有数据)
DECLARE @i INT = 1;
WHILE @i <= 100000
BEGIN
INSERTINTO#Employees VALUES (@i, 5000.00);
SET @i = @i + 1;
END
-- 现在开始分批涨薪
DECLARE @BatchSize INT = 1000; -- 每批处理1000条
DECLARE @ProcessedRows INT = 1; -- 已处理行数
DECLARE @TotalRows INT;
SELECT @TotalRows = COUNT(*) FROM#Employees; -- 总行数
WHILE @ProcessedRows <= @TotalRows
BEGIN
-- 更新当前批次
UPDATE TOP (@BatchSize) #Employees
SET Salary = Salary * 1.05-- 涨薪5%
WHERE EmployeeID BETWEEN @ProcessedRows AND @ProcessedRows + @BatchSize - 1;
PRINT '已处理 ' + CAST(@BatchSize AS VARCHAR) + ' 条记录,当前进度:'
+ CAST(@ProcessedRows AS VARCHAR) + '/' + CAST(@TotalRows AS VARCHAR);
-- 关键:更新已处理行数
SET @ProcessedRows = @ProcessedRows + @BatchSize;
-- 可选:每批之间稍微暂停一下,减轻服务器压力
WAITFOR DELAY '00:00:00.100'; -- 暂停0.1秒
END
PRINT '批量涨薪完成!';
这样做的优点:
- 减少锁竞争:每次只锁一小部分数据,其他事务可以访问表的其他部分。
- 可以中断:如果发现问题,可以停止脚本,不会全部回滚。
三、WHILE循环的“好朋友”:BREAK 和 CONTINUE
有时候你需要更精细地控制循环。
DECLARE @i INT = 1;
WHILE @i <= 10
BEGIN
IF @i = 6
BEGIN
PRINT '遇到6,提前退出!';
BREAK; -- 直接跳出循环
END
PRINT '当前值:' + CAST(@i AS VARCHAR);
SET @i = @i + 1;
END
-- 只会打印1到5,然后退出
DECLARE @i INT = 0;
WHILE @i < 10
BEGIN
SET @i = @i + 1;
IF @i % 2 = 0 -- 如果是偶数
BEGIN
PRINT '跳过偶数:' + CAST(@i ASVARCHAR);
CONTINUE; -- 跳过下面的代码,直接开始下一次循环
END
PRINT '处理奇数:' + CAST(@i ASVARCHAR);
END
-- 会打印所有奇数,但偶数只打印“跳过偶数”
四、WHILE循环的“噩梦”:无限循环
忘记更新循环条件是最常见的错误:
-- ⚠️ 危险代码!不要在生产环境运行!
DECLARE @i INT = 1;
WHILE @i <= 5 -- 条件永远为真,因为@i从未改变
BEGIN
PRINT '无限循环...';
-- 忘记写:SET @i = @i + 1;
END
防御措施: 总是为循环设置安全上限:
DECLARE @i INT = 1;
DECLARE @SafetyCounter INT = 0;
WHILE @i <= 1000000 AND @SafetyCounter < 1000 -- 安全阀:最多1000次
BEGIN
-- 你的业务逻辑...
SET @i = @i + 1;
SET @SafetyCounter = @SafetyCounter + 1;
END
IF @SafetyCounter >= 1000
PRINT '警告:达到安全上限,循环可能有问题!';
五、WHILE vs 游标:什么时候用哪个?
很多人用 WHILE 来实现游标的功能,但两者有区别:
简单原则:
- 如果只是重复执行某个操作直到条件满足,用
WHILE。 - 如果能用基于集合的操作(一句UPDATE),永远不要用
WHILE 或游标。
六、一个有趣的例子:生成日期序列
-- 生成2024年5月1日到5月31日的所有日期
DECLARE @StartDate DATE = '2024-05-01';
DECLARE @EndDate DATE = '2024-05-31';
CREATETABLE#DateList (TheDate DATE);
WHILE @StartDate <= @EndDate
BEGIN
INSERTINTO#DateList VALUES (@StartDate);
SET @StartDate = DATEADD(DAY, 1, @StartDate); -- 日期加1天
END
SELECT * FROM#DateList ORDER BY TheDate;
DROPTABLE#DateList;
总结:WHILE循环的正确打开方式
WHILE 循环就像 SQL 工具箱里的“手动挡”。自动挡(集合操作)开起来更爽,但在某些特殊路况下,手动挡能给你更精细的控制。
阅读原文:原文链接
该文章在 2025/12/31 10:26:45 编辑过