SQL Server中DECLARE CURSOR基本写法为:DECLARE 游标名 CURSOR [LOCAL | GLOBAL] [FORWARD_ONLY | SCROLL] [STATIC | KEYSET | DYNAMIC | FAST_FORWARD] FOR SELECT语句;必须配合OPEN、FETCH NEXT INTO、WHILE @@FETCH_STATUS=0循环、CLOSE和DEALLOCATE使用,推荐优先选用LOCAL FAST_FORWARD以提升性能。
游标不是必须的,但当你需要逐行处理结果集(比如根据某字段值动态计算并更新另一字段),且逻辑无法用单条 UPDATE ... JOIN 或窗口函数替代时,游标才真正有用。SQL Server 的显式游标需明确声明、打开、取值、移动、关闭和释放。
常见错误是漏掉 DEALLOCATE,导致后续执行时报错 The cursor is already declared;或在循环中未用 FETCH NEXT 更新 @variable,造成无限循环。
实操建议:
LOCAL FAST_FORWARD 声明游标(只读、前向、轻量):DECLARE cur_update CURSOR LOCAL FAST_FORWARD FOR SELECT id, amount FROM orders WHERE status = 'pending'
id 是 INT,就别用 @id VARCHAR(10)
@@FETCH_STATUS = 0,否则可能处理到空行或上一轮残留值游标本身不带更新能力,UPDATE 语句仍要靠主键或唯一约束精准定位记录。若在循环里写 UPDATE orders SET ... WHERE status = 'pending',每次都会扫全表符合条件的行——这不是“更新当前行”,而是反复更新整个结果集。
典型场景:对每个待处理订单,按实时汇率换算 amount_usd 字段。
实操建议:
id),并在 UPDATE 的 WHERE 中使用它:UPDATE orders SET amount_usd = @amount * @exchange_rate WHERE id = @id
UPDATE ... FROM 替代游标,性能通常高一个数量级MySQL 不支持 @@FETCH_STATUS,必须用 DECLARE CONTINUE HANDLER FOR NOT FOUND 捕获游标耗尽信号。漏写 handler 会导致循环一次后直接退出,只处理首行。
错误现象:存储过程执行完,只更新了第一条记录,控制台无报错。
实操建议:
OPEN 之前声明,且变量(如 @done)要在 handler 内赋值:DECLARE done INT DEFAULT FALSE;<br>DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
done,而不是等下次循环开始才判断:FETCH cur INTO @id, @amount;<br>IF done THEN LEAVE read_loop; END IF;
90% 的“需要游标”需求,本质是没想清楚数据操作是否可集合化。比如“给每个用户发通知”,不该游标查用户再调存储过程发邮件,而应生成通知记录表,由后台任务异步消费。
容易被忽略的点:
UPDATE ... FROM(SQL Server)或 UPDATE ... JOIN(MySQL)能解决大部分“查-算-改”链路WHILE 循环(SQL Server)比游标快,因省去游标开销,且可加索引