编程SQL 开发人员

描述使用 SQL 游标(CURSOR)进行数据序列处理的特点、优势和潜在问题。在何种情况下使用游标是合适的,何时应避免它们而使用基于集合(set-based)操作?请举一个使用游标的例子。

用 Hintsage AI 助手通过面试

回答。

游标 在 SQL 中允许逐行遍历数据集并对每条记录执行特定操作,这类似于编程中的迭代。这对于无法通过单个 SQL 查询实现的复杂逐步逻辑非常方便(例如,具有外部状态变化的逐步计算)。

游标示例:

DECLARE my_cursor CURSOR FOR SELECT id, balance FROM Accounts WHERE isActive = 1; OPEN my_cursor; DECLARE @id INT, @bal DECIMAL(10,2); FETCH NEXT FROM my_cursor INTO @id, @bal; WHILE @@FETCH_STATUS = 0 BEGIN UPDATE Accounts SET balance = @bal * 1.05 WHERE id = @id; FETCH NEXT FROM my_cursor INTO @id, @bal; END CLOSE my_cursor; DEALLOCATE my_cursor;

优点:

  • 允许实现复杂的过程,其中每一行需要单独处理或检查外部条件。
  • 方便与旧系统或迁移集成,需要逐步控制的场景。

缺点和潜在问题:

  • 游标通常非常慢:逐条处理,而不是基于集合的操作;
  • 占用大量资源,可能会锁定表;
  • 不适合大量数据的扩展;
  • 更好地重写为基于集合的 SQL 或使用批处理。

迷惑性问题。

可以在触发器内部使用游标吗?这可能对数据库性能产生什么影响?

回答和示例: 在触发器内部使用游标是可能的,但极不推荐——每个 DML 操作可能会为每个受影响的行启动游标,这会导致请求和锁定的激增。

CREATE TRIGGER UpdateBalance ON Accounts AFTER INSERT AS DECLARE c CURSOR FOR SELECT id FROM inserted; -- 不好! OPEN c; -- ...

历史

项目:零售订单摘要。 任务是计算库存余量,如果发现批次存在缺陷——后续批次需要通过更新折扣手动重新计算。使用游标遍历批次。后来发现,在多次运行时,线程被阻塞了几个小时,服务器负载呈指数增长,锁定争用导致了故障。


历史

项目:ERP,数据迁移。 在导入处理程序中添加了通过游标处理错误的功能。没有考虑到在 500 万行上,游标的效率比使用基于集合的更新 + CASE 的批处理慢了 40 倍。由于迁移缓慢,截止日期被推迟。


历史

项目:财务公司的账单。 在资金变动表的更新触发器中添加了游标,以计算新的聚合余额。在生产环境中,这导致了“停止世界”——插入即使是一条记录也因为启动嵌套游标在许多行上而减缓了服务的运行。