SQL Server里那些游标到底有几种,怎么用还挺让人头疼的讲解
- 问答
- 2026-01-18 09:19:25
- 4
说到SQL Server里的游标,这确实是个让很多刚开始接触数据库编程,甚至是一些老手都偶尔会头疼的话题,它不像普通的SQL语句那样直接了当,用起来感觉有点绕,但它在处理某些特定问题时又是无可替代的,咱们今天就把它掰开揉碎了讲讲,目的就是让它不再那么“让人头疼”。
游标到底是什么?为什么需要它?
你可以把游标想象成一个“指针”或者“书签”,我们平时写的SELECT语句,比如SELECT * FROM Customers,一下子就把所有客户数据都返回了,像一张大饼摊在你面前,但有时候,我们不想或者不能一次性处理所有数据,而是想一行一行地、有顺序地检查或操作这些数据,这时候,游标就派上用场了。
游标允许你在一个结果集(就是那条SELECT语句查出来的数据)里,一行一行地移动,然后对每一行数据进行单独的处理,这就好比你看书不用扫读,而是用手指着一行一行地读,读一行,思考一下,再做点笔记。
什么时候会用到这种“一行一行处理”的笨办法呢?常见的场景有:
- 逐行计算或校验:需要根据上一行的某个计算结果,来决定下一行该如何处理。
- 复杂的业务逻辑:某些业务规则非常复杂,无法用一条简单的UPDATE或INSERT语句完成,需要根据每一行的不同情况执行不同的SQL操作。
- 维护操作:比如需要遍历所有数据库的表,逐个生成统计信息或执行检查。
游标的几种主要类型(到底有几种?)
头疼的地方来了,游标不是一种,而是有好几种,它们的区别主要在于怎么移动、别人能不能看到你修改的数据等等,根据微软官方文档(来源:Microsoft Learn - DECLARE CURSOR (Transact-SQL)),我们可以从几个角度来分类:
-
根据滚动能力(能不能往后走?):
- 只进游标:这是最简单、也是最常用的一种,它就像录音机的播放键,只能从结果集的第一行一直走到最后一行,不能回头,它是性能最好的选择,如果你只需要遍历一次数据,就用它。
- 滚动游标:这个就高级了,像音乐播放器的进度条,你不仅可以向前走,还可以向后滚,可以直接跳到第一行或最后一行,甚至可以跳到相对当前位置的某一行(往后跳3行”),功能强大,但代价是消耗更多的资源,速度也更慢。
-
根据对底层数据的敏感性(别人改数据会影响你吗?):
- 静态游标:当你声明一个静态游标时,SQL Server会把查询结果集复制一份到一个临时的工作表里,你之后在这个游标里的所有操作,都是针对这个“快照”进行的,在此期间,即使其他用户修改、删除了原始表中的数据,你也完全看不到,你的游标数据不会变,这保证了数据的一致性,但可能不是最新的。
- 动态游标:和静态完全相反,你在游标中看到的永远是最新的数据,任何用户对底层数据做的任何修改(增删改),你都能在滚动游标时立即看到,这是最“敏感”的游标,资源开销最大,也最容易因为数据变化而产生意想不到的结果。
- 键集驱动游标:可以理解为一种折中方案,当你打开游标时,SQL Server只把能唯一标识每一行的“键”(通常是主键)存起来,当你通过游标获取某一行时,它才根据这个键去当前的实际数据表里读取数据,这意味着,游标能感知到别人对行数据的修改(因为你读取的是最新值),但如果别人新插入了行,你看不到;别人删除了行,你再去取时会发现取不到,它是性能和实时性的一种平衡。
-
根据是否可更新(能直接改数据吗?):
- 只读游标:顾名思义,只能用来读取数据,不能通过它来更新或删除数据。
- 可更新游标:允许你通过游标直接更新或删除当前所在行的数据,你在声明游标时可以用
FOR UPDATE子句来指定。
这些类型是可以组合的,比如你可以声明一个“只进的、静态的、只读游标”,这也是最常见的默认组合。
游标的基本使用步骤(怎么用?)
使用游标有一个非常固定的“套路”,记住这个步骤就能搞定大部分情况:
-
声明游标:使用
DECLARE语句,定义游标的名称和它要使用的SELECT查询,你可以指定游标的类型(比如SCROLL、STATIC等)。DECLARE customer_cursor CURSOR FOR SELECT CustomerID, CustomerName FROM Customers WHERE Country = 'China';
-
打开游标:使用
OPEN语句,这时SQL Server才会真正执行查询,准备好结果集。OPEN customer_cursor;
-
获取数据:使用
FETCH语句,从游标中提取一行数据,你通常会把提取的数据放入变量中,以便后续处理。-- 先定义变量 DECLARE @CustID INT, @CustName NVARCHAR(50); -- 然后获取第一行数据 FETCH NEXT FROM customer_cursor INTO @CustID, @CustName;
-
循环处理:这是一个关键步骤,你需要在一个
WHILE循环里,不断地FETCH数据,直到没有更多数据为止,SQL Server提供了一个全局变量@@FETCH_STATUS来告诉你上次FETCH的状态,如果等于0表示成功获取到了一行。WHILE @@FETCH_STATUS = 0 BEGIN -- 在这里处理你的业务逻辑,比如打印变量,或者执行其他SQL PRINT '客户ID: ' + CAST(@CustID AS NVARCHAR(10)) + ', 客户名称: ' + @CustName; -- 获取下一行! FETCH NEXT FROM customer_cursor INTO @CustID, @CustName; END -
关闭游标:处理完后,用
CLOSE语句释放游标当前的结果集和相关锁。CLOSE customer_cursor;
-
释放游标:最后一步,用
DEALLOCATE语句彻底从内存中清除游标,释放所有资源。DEALLOCATE customer_cursor;
为什么让人头疼?以及最重要的建议
游标头疼的点在于:
- 复杂:步骤繁琐,容易忘记某一步(比如忘了关闭和释放)。
- 性能差:因为是一行一行处理,相当于把集合操作(SQL的强项)变成了过程化操作(程序的强项),会产生大量的开销,数据库引擎优化的是批量处理,而不是单行循环,滥用游标会导致程序运行极慢。
最重要的建议是:尽量避免使用游标!
在99%的情况下,你遇到的问题都可以用更高效的标准SQL集合操作来解决,比如使用CASE语句、子查询、连接(JOIN)、窗口函数甚至临时表,在决定使用游标之前,一定要扪心自问:“我真的不能用一条UPDATE或一个带CASE的SELECT来解决吗?”
只有当你的业务逻辑确实复杂到必须依赖行的顺序或需要逐行进行不同操作时,游标才是你最后的选择,把它当作工具箱里的一把特殊螺丝刀,平时用不到,但某些特定场合非它不可,理解了它的原理和代价,你就能在头疼的时候,做出正确的选择。

本文由度秀梅于2026-01-18发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:http://www.haoid.cn/wenda/82952.html
