学习sql server 游标
在 SQL Server 中,游标是一种逐行处理数据的机制。它允许你像编程语言中的循环一样,一行一行地遍历查询结果集,并对每一行执行特定的操作。
核心概念:为什么需要游标?
SQL 是集合操作语言(一次处理一组数据),但在某些场景下,你需要逐行处理:
- 对每一行执行不同的复杂计算(例如,计算移动总和、基于上一行数据更新当前行)
- 调用存储过程,且需要将每一行的列作为参数传入
- 需要根据前一行数据的状态来决定当前行的操作
这时,游标就派上了用场。
SQL Server 游标的工作原理:五步生命周期
使用游标通常遵循以下五个标准步骤:
- DECLARE CURSOR(声明游标):定义游标所基于的 SELECT 查询语句。
- OPEN(打开游标):执行查询,将结果集填充到游标中。
- FETCH(提取行):从游标中获取一行数据到变量中。
- WHILE 循环处理:使用
@@FETCH_STATUS系统变量判断是否还有数据(0 表示成功),在循环内处理业务逻辑。 - CLOSE & DEALLOCATE(关闭并释放):释放游标占用的资源。
一个完整的基础示例
假设有一个 Employees 表,我们要给每位员工加薪 10%。
-- 声明变量用于存储每行的数据
DECLARE @EmployeeID INT, @CurrentSalary DECIMAL(10,2);
-- 1. 声明游标
DECLARE SalaryCursor CURSOR FOR
SELECT EmployeeID, Salary FROM Employees;
-- 2. 打开游标
OPEN SalaryCursor;
-- 3. 获取第一行
FETCH NEXT FROM SalaryCursor INTO @EmployeeID, @CurrentSalary;
-- 4. 开始循环处理每一行
WHILE @@FETCH_STATUS = 0
BEGIN
-- 业务逻辑:更新当前行的工资
UPDATE Employees
SET Salary = @CurrentSalary * 1.10
WHERE EmployeeID = @EmployeeID;
-- 获取下一行
FETCH NEXT FROM SalaryCursor INTO @EmployeeID, @CurrentSalary;
END
-- 5. 关闭并释放游标
CLOSE SalaryCursor;
DEALLOCATE SalaryCursor;
游标的四种类型(性能差异巨大)
在 DECLARE CURSOR 时,你可以指定类型,直接影响性能和资源占用:
| 类型 | 特点 | 性能 | 典型场景 |
|---|---|---|---|
| FAST_FORWARD | 只读、只能向前滚动 | 最快(默认首选) | 只需要单向遍历数据,不修改数据 |
| READ_ONLY | 只读,不能修改数据 | 很快 | 只需要读取数据做计算或展示 |
| SCROLL | 可前后滚动、可更新 | 较慢,占用较多资源 | 需要随机访问行(如向前/向后跳跃) |
| DYNAMIC | 反映所有数据更改 | 最慢,资源开销大 | 需要看到其他用户对数据的实时修改 |
最佳实践与性能警告
- 性能杀手:游标将基于集合的高效操作(一次处理几千行)变成逐行处理(几千次独立操作),可能导致性能下降几十甚至上百倍。在 SQL Server 中,循环是万恶之源。
- 优先使用基于集合的操作:能用一条 UPDATE 语句解决的问题,绝不用游标。例如上面的加薪示例,完全可以用
UPDATE Employees SET Salary = Salary * 1.10一行代码完成,效率高得多。 - 游标的替代方案:
- 使用窗口函数:如
ROW_NUMBER(),LAG(),LEAD()可以替代许多需要逐行比较或累积计算的游标。 - 使用递归 CTE:处理层次结构数据(如组织结构、菜单树)。
- 使用临时表 + WHILE 循环:有时比游标效率稍高,但仍不如集合操作。
- 使用窗口函数:如
什么时候真的需要使用游标?
尽管有性能问题,少数场景游标仍然是合适的选择:
- 调用需要逐行参数的存储过程:存储过程不接受集合参数,只能为每一行调用一次。
- 复杂的逐行累计计算:当前行的值依赖于前一行的计算结果,且无法用
LAG/LEAD表达(例如带有复杂业务规则的移动窗口)。 - 分批处理大表:为了避免长事务阻塞或日志暴涨,可以用游标每次处理 1000 行,然后
COMMIT。
总结
- 记住:SQL 是基于集合的语言,优先思考“如何一次处理整个集合”。
- 怀疑:当你觉得“可能需要游标”时,先停下来,搜索能否用
UPDATE、窗口函数或CTE替代。 - 最后手段:当确定无法避免时,使用
FAST_FORWARD类型获得最佳性能,并确保游标循环内的操作尽量高效。
网站:http://shibowl.top
github:https://github.com/hanbinjxnc
博客园:https://www.cnblogs.com/hool
博客:https://blog.shibowl.top
淘宝店:https://boxunwl.taobao.com/
作者:世博 2019年4月28日----
github:https://github.com/hanbinjxnc
博客园:https://www.cnblogs.com/hool
博客:https://blog.shibowl.top
淘宝店:https://boxunwl.taobao.com/
作者:世博 2019年4月28日----
浙公网安备 33010602011771号