问题的背景:
标准 SQL 最初被设计为一种声明性语言,不包含经典编程元素(循环、分支、直接跳转),但通过 T-SQL/PLSQL 等扩展可以使用类似 WHILE、CASE 甚至模仿 GOTO 的结构。
问题:
迭代操作(逐条处理记录的循环)通常会导致性能下降,特别是在处理大量数据时,如果不采用“基于集”(set-based)的方法。分支、CASE、IF 非常方便,但过度嵌套会影响代码的可读性和可预测性。
解决方案:
使用控制结构应仅在无法替代批量(bulk/set)处理时才合理!对于复杂计算——可以使用小循环、触发器或 CASE。对于大量处理——最好使用窗口函数或带子查询的 UPDATE。
示例代码 (T-SQL):
DECLARE @i INT = 1 WHILE (@i <= 5) BEGIN IF @i % 2 = 0 PRINT CONCAT('偶数: ', @i) ELSE PRINT CONCAT('奇数: ', @i) SET @i = @i + 1 END
CASE 表达式:
SELECT num, CASE WHEN num % 2 = 0 THEN 'even' ELSE 'odd' END AS parity FROM numbers
关键特性:
CASE 可以用于过滤行,就像 WHERE 吗?
不可以!CASE 返回不同的值,但不过滤行。常见错误是通过 CASE “过滤”而不是使用 WHERE,结果将是错误的。
WHILE 和 CURSOR 有什么区别 — 不是一回事吗?
WHILE 是基本循环,使用用户控制的变量;CURSOR 按表记录工作,保持对行的引用。CURSOR 更占用资源,通常在处理大数据时运行得更慢。
对于大规模处理:WHILE 循环配合 UPDATE 和一个基于集的 UPDATE 哪个更快?
在 99% 的情况下,一个基于集的 UPDATE(或 INSERT)比逐条记录的循环快得多(即使看起来更“灵活”)。
-- 错误的方法 DECLARE @id INT = 1 WHILE (@id <= 100000) BEGIN UPDATE t SET flag=1 WHERE id=@id SET @id = @id + 1 END -- 正确 UPDATE t SET flag=1 WHERE id BETWEEN 1 AND 100000
在一个项目中,为了更新一百万个订单的状态,写了循环对每个 id 进行 UPDATE。处理时间长达 ~8 小时。当在一半时出错 — 一切丢失,必须手动修复。
优点:
缺点:
将其改为基于集的 UPDATE,使用单个表达式。执行时间缩短到 6 分钟,处理是原子的。
优点:
缺点: