DECLARE cursor_goods CURSOR FOR /*声明游标*/ SELECT goods_id,goods_name,store_count FROM goods WHERE store_count BETWEEN 500 AND 999 /*定义游标结果集*/ OPEN cursor_goods /*打开游标*/ FETCH NEXT FROM cursor_goods /*执行取数操作*/ WHILE @@fetch_status = 0 /*判断是否还可以继续取数*/ BEGIN FETCH NEXT FROM cursor_goods END CLOSE cursor_goods /*关闭游标*/ DEALLOCATE cursor_goods /*释放游标*/
SQL Server 游标代码逐行详解
这是一个标准的 SQL Server 游标操作流程,用于逐行处理查询结果集。让我从整体到细节讲清楚。
一、整体功能概述
这段代码的作用是:
遍历
goods表中库存量在 500-999 之间的商品,并逐行取出商品信息
注意:代码中 WHILE 循环里只执行了 FETCH,没有实际处理数据的语句。实际开发中会在循环内添加业务逻辑。
二、游标的五个标准步骤
text
┌─────────────────────────────────────────────────────────────┐ │ 步骤1:DECLARE CURSOR → 声明游标(定义要查询什么) │ │ ↓ │ │ 步骤2:OPEN → 打开游标(执行查询,准备取数据) │ │ ↓ │ │ 步骤3:FETCH → 取一行数据 │ │ ↓ │ │ 步骤4:WHILE 循环 → 重复取数据直到取完 │ │ ↓ │ │ 步骤5:CLOSE → 关闭游标(释放结果集) │ │ ↓ │ │ 步骤6:DEALLOCATE → 释放游标(删除游标定义) │ └─────────────────────────────────────────────────────────────┘
三、逐行详细讲解
第1行:DECLARE cursor_goods CURSOR FOR
sql
DECLARE cursor_goods CURSOR FOR
| 部分 | 含义 |
|---|---|
DECLARE | 声明(拉丁语 declarare) |
cursor_goods | 游标名称(goods 游标) |
CURSOR | 游标类型 |
FOR | 为了(指向后面的 SELECT 语句) |
作用:告诉 SQL Server,我要声明一个名叫 cursor_goods 的游标,它要执行的查询是后面的 SELECT 语句。
第2-3行:SELECT ... FROM ... WHERE ...
sql
SELECT goods_id,goods_name,store_count FROM goods WHERE store_count BETWEEN 500 AND 999
| 部分 | 含义 |
|---|---|
goods_id | 商品编号 |
goods_name | 商品名称 |
store_count | 库存数量 |
FROM goods | 从商品表查询 |
WHERE store_count BETWEEN 500 AND 999 | 筛选库存在 500-999 之间的商品 |
作用:定义游标的结果集——哪些数据可以被逐行遍历。
第4行:OPEN cursor_goods
sql
OPEN cursor_goods
| 单词 | 词源 | 含义 |
|---|---|---|
| OPEN | 古英语 openian | 打开、开启 |
作用:
执行游标关联的 SELECT 语句
把结果集准备好
游标指针指向第一行之前
第5行:FETCH NEXT FROM cursor_goods
sql
FETCH NEXT FROM cursor_goods
| 部分 | 含义 |
|---|---|
FETCH | 取(古英语 fetian) |
NEXT | 下一行 |
FROM | 从... |
cursor_goods | 游标名称 |
作用:
从游标中取第一行数据
然后游标指针自动下移
重要:如果想把取出的数据存到变量里,应该这样写:
sql
FETCH NEXT FROM cursor_goods INTO @id, @name, @count
第6行:WHILE @@fetch_status = 0
sql
WHILE @@fetch_status = 0
| 部分 | 含义 |
|---|---|
WHILE | 当...的时候(古英语 hwīl) |
@@fetch_status | 系统全局变量,记录上次 FETCH 的状态 |
= 0 | 等于 0 表示 FETCH 成功取到了数据 |
作用:只要上次 FETCH 操作成功(还有数据可读),就继续循环。
@@fetch_status 的三种状态
| 返回值 | 含义 |
|---|---|
0 | 成功取到一行数据 |
-1 | 取数据失败(如行已删除) |
-2 | 取到的是丢失的行的数据 |
第7行:BEGIN
sql
BEGIN
作用:标记循环体的开始。
第8行:FETCH NEXT FROM cursor_goods
sql
FETCH NEXT FROM cursor_goods
作用:取下一行数据,并更新 @@fetch_status。
问题:这里没有 INTO 子句,取出的数据没有保存。
本意应该是:
sql
FETCH NEXT FROM cursor_goods INTO @goods_id, @goods_name, @store_count
第9行:END
sql
END
作用:标记循环体的结束。程序会跳回 WHILE 继续判断。
第10行:CLOSE cursor_goods
sql
CLOSE cursor_goods
| 单词 | 词源 | 含义 |
|---|---|---|
| CLOSE | 拉丁语 clausus | 关闭 |
作用:
关闭游标
释放结果集占用的内存
游标定义还在,可以重新
OPEN
第11行:DEALLOCATE cursor_goods
sql
DEALLOCATE cursor_goods
| 单词 | 词源 | 含义 |
|---|---|---|
| DEALLOCATE | de-(移除)+ allocate(分配) | 释放、解除分配 |
作用:
彻底删除游标定义
释放所有相关资源
之后不能再
OPEN
四、完整的正确写法示例
sql
-- 声明变量存放取出的数据DECLARE @goods_id INT, @goods_name NVARCHAR(50), @store_count INT-- 1. 声明游标DECLARE cursor_goods CURSOR FOR SELECT goods_id, goods_name, store_count FROM goods WHERE store_count BETWEEN 500 AND 999-- 2. 打开游标OPEN cursor_goods-- 3. 取第一行数据FETCH NEXT FROM cursor_goods INTO @goods_id, @goods_name, @store_count-- 4. 循环处理WHILE @@FETCH_STATUS = 0BEGIN -- 在这里处理每一行数据 PRINT '商品:' + @goods_name + ',库存:' + CAST(@store_count AS VARCHAR) -- 取下一行 FETCH NEXT FROM cursor_goods INTO @goods_id, @goods_name, @store_countEND-- 5. 关闭游标CLOSE cursor_goods-- 6. 释放游标DEALLOCATE cursor_goods
五、各步骤状态变化图
text
┌─────────────────────────────────────────────────────────────┐ │ DECLARE │ │ cursor_goods 定义好了,但没有数据 │ ├─────────────────────────────────────────────────────────────┤ │ OPEN │ │ 执行查询,结果集:第1行、第2行、第3行... │ │ 指针指向 [第1行之前] │ ├─────────────────────────────────────────────────────────────┤ │ FETCH INTO │ │ 取第1行 → 变量得到值 → 指针指向 [第1行和第2行之间] │ │ @@FETCH_STATUS = 0(成功) │ ├─────────────────────────────────────────────────────────────┤ │ WHILE 循环 │ │ 处理第1行 → FETCH 第2行 → 处理第2行 → ... │ │ 取完最后一行后,再 FETCH 时 @@FETCH_STATUS ≠ 0 → 退出循环 │ ├─────────────────────────────────────────────────────────────┤ │ CLOSE │ │ 游标关闭,结果集释放 │ ├─────────────────────────────────────────────────────────────┤ │ DEALLOCATE │ │ 游标定义删除,彻底清理 │ └─────────────────────────────────────────────────────────────┘
六、为什么原代码有个细节问题?
原代码:
sql
FETCH NEXT FROM cursor_goods
问题:没有 INTO 子句,取出的数据没有地方存放。
正确写法:
sql
FETCH NEXT FROM cursor_goods INTO @变量1, @变量2, @变量3
七、词源总结
| 关键字 | 词源 | 含义 |
|---|---|---|
| CURSOR | 拉丁语 cursor(奔跑者) | 游标(在数据上"跑") |
| DECLARE | 拉丁语 declarare | 声明 |
| OPEN | 古英语 openian | 打开 |
| FETCH | 古英语 fetian | 取来 |
| CLOSE | 拉丁语 clausus | 关闭 |
| DEALLOCATE | de- + allocate | 释放、解除分配 |
八、总结
| 步骤 | 代码 | 作用 |
|---|---|---|
| 1 | DECLARE ... CURSOR FOR | 声明游标,定义要遍历的数据集 |
| 2 | OPEN | 打开游标,执行查询 |
| 3 | FETCH NEXT | 取第一行数据 |
| 4 | WHILE @@FETCH_STATUS = 0 | 判断是否还有数据 |
| 5 | BEGIN...END | 循环体(处理数据 + 取下一行) |
| 6 | CLOSE | 关闭游标,释放结果集 |
| 7 | DEALLOCATE | 释放游标定义,彻底清理 |
一句话总结:游标的本质是一个数据遍历器——先 DECLARE 定义要取什么数据,再 OPEN 打开它,然后 FETCH 一行一行地取,用 WHILE 循环处理每一行,最后 CLOSE 和 DEALLOCATE 释放资源。这是一种逐行处理而非批量处理的方式。
⚠️ 性能提示:游标在处理大量数据时效率较低,能用集合操作(UPDATE、DELETE、INSERT...SELECT)时尽量不要用游标。只有在需要逐行进行复杂业务逻辑时才使用游标