嵌套游标

 今天有人询问游标是否可以嵌套使用的问题,因为用于标识游标是否已经达到数据集结尾的标志值@@FETCH_STATUS是一个全局变量,那么如果出现游标嵌套的话可能造成内侧的游标完成数据集遍历以后导致外侧游标的无法再循环读取下一行的数据。先来看看SQL Server 2000中联机帮助的内容:


@@FETCH_STATUS

返回被 FETCH 语句执行的最后游标的状态,而不是任何当前被连接打开的游标的状态。

返回值描述
0FETCH 语句成功。
-1FETCH 语句失败或此行不在结果集中。
-2被提取的行不存在。

语法

@@FETCH_STATUS

返回类型

integer

注释

由于 @@FETCH_STATUS 对于在一个连接上的所有游标是全局性的,要小心使用 @@FETCH_STATUS 。在执行一条 FETCH 语句后,必须在对另一游标执行另一 FETCH 语句前测试 @@FETCH_STATUS 。在任何提取操作出现在此连接上前,@@FETCH_STATUS 的值没有定义。

例如,用户从一个游标执行一条 FETCH 语句,然后调用一个存储过程,此存储过程打开并处理另一个游标的结果。当控制从被调用的存储过程返回后,@@FETCH_STATUS 反映的是在存储过程中执行的最后的 FETCH 语句的结果,而不是在存储过程被调用之前的 FETCH 语句的结果。

示例

下面的示例用 @@FETCH_STATUS 控制在一个 WHILE 循环中的游标活动。

DECLARE Employee_Cursor CURSOR FOR
SELECT LastName, FirstName FROM Northwind.dbo.Employees
OPEN Employee_Cursor
FETCH NEXT FROM Employee_Cursor
WHILE @@FETCH_STATUS = 0
BEGIN
   
FETCH NEXT FROM Employee_Cursor
END
CLOSE Employee_Cursor
DEALLOCATE Employee_Cursor

从最开始的那句话“返回被 FETCH 语句执行的最后游标的状态,而不是任何当前
被连接打开的游标的状态。”
可以看出,@@FETCH_STATUS这个全局变量的确不是和某个特定的游标绑定在一起
的,而是一个类似于我们声明的静态变量一样,谁都可以去修改它的值,而且一旦它
的值出现了修改,那么所有使用到这个变量的对象都会受到影响。的确游标嵌套很容
易引起混乱,但是游标的嵌套是可能的,原理也是利用上面这句话,如果我们在执行
完成了内侧的游标后,先通过FETCH语句执行外侧的游标,这样如果外侧还存在记录
没有遍历完成的话,那么此时@@FETCH_STATUS又会被重新设置为0,外侧的游标
也可以继续进行遍历操作了。所以说解决问题的关键还是看FETCH语句的摆放位置。
例如如下代码:
    --外侧游标(mc_AffectedEmployee);
    DECLARE mc_AffectedEmployee CURSOR LOCAL FOR         --声明一个局部游标;
        SELECT DISTINCT infectRows.C_EMPLPOST_EMPLID FROM (
            
SELECT C_EMPLPOST_EMPLID FROM INSERTED
            
UNION
            
SELECT C_EMPLPOST_EMPLID FROM DELETED) infectRows
    
--打开游标;
    OPEN mc_AffectedEmployee;
    
    
--这里需要先执行一次FETCH语句,以便查看是否需要进行循环遍历;(这里很像IDataReader.Read()方法)
    FETCH NEXT FROM mc_AffectedEmployee INTO @EmployeeID
    
WHILE(@@FETCH_STATUS = 0)
        
BEGIN
                                                                
--Some T-SQL Statements here
            
            
--声明内部游标(mc_AllPostID)
            DECLARE mc_AllPostID CURSOR LOCAL FOR
                
SELECT C_EMPLPOST_POSTID FROM T_PERSONNEL_EMPLPOSITION WHERE C_EMPLPOST_EMPLID = @EmployeeID AND C_EMPLPOST_STARTDATE <= getDate() AND ((C_EMPLPOST_ENDDATE >= getDate()) OR (C_EMPLPOST_ENDDATE IS NULL)) AND C_EMPLPOST_DEPTID = @CurrentOrgCode;

            
OPEN mc_AllPostID

                                                                
--同样这里需要先执行一次FETCH语句;
            FETCH NEXT FROM mc_AllPostID INTO @NowPostID;
            
WHILE (@@FETCH_STATUS = 0)
                
BEGIN
                                                                                                          
--Some T-SQL Statements here.
                                                                                                          -- 这里需要执行FETCH语句为下次循环设置@@FETCH_STATUS;
                    FETCH NEXT FROM mc_AllPostID INTO @NowPostID;
                
END
            
UPDATE T_PERSONNEL_EMPLINFO SET C_EMPLINFO_SHOWPOSTNAME = @ShowPostName WHERE C_EMPLINFO_EMPLID = @EmployeeID;
            
CLOSE mc_AllPostID;
            
DEALLOCATE mc_AllPostID;
            
--***************************************************************************
            --这里判断是否已经完成了数据集的遍历,如果已经完成那么就将循环标志值(@Loop)设置为假(0);
                                --由于内侧的游标已经完成遍历,所以这里@@FETCH_STATUS  = -1,通过FETCH语句,如果此时外侧的游标还存在没有遍历的记录,那么这里@@FETCH_STATUS又会被重新赋值为0,以便下次循环操作。
            FETCH NEXT FROM mc_AffectedEmployee INTO @EmployeeID
        
END
        
    
--关闭游标;
    CLOSE mc_AffectedEmployee;
    
DEALLOCATE mc_AffectedEmployee;
### Oracle 游标嵌套游标的语法 在 PL/SQL 中,可以通过定义多个显式游标来实现游标嵌套的功能。外层游标用于获取一组记录,而内层游标则基于外层游标的结果进一步查询并处理数据。 以下是关于如何编写游标嵌套的示例: #### 外层游标与内层游标 在外层游标中遍历父表的数据,而在内层游标中通过子查询关联到其他表或同一表的不同字段集合。这种设计通常适用于多级关系型数据库结构[^1]。 ```plsql DECLARE -- 定义外层游标 CURSOR outer_cursor IS SELECT deptno FROM dept; -- 定义内层游标 CURSOR inner_cursor(dept_number NUMBER) IS SELECT empno, ename, sal FROM emp WHERE deptno = dept_number; BEGIN FOR rec_outer IN outer_cursor LOOP DBMS_OUTPUT.PUT_LINE('部门号: ' || rec_outer.deptno); -- 打开内层游标 FOR rec_inner IN inner_cursor(rec_outer.deptno) LOOP DBMS_OUTPUT.PUT_LINE( '员工编号: ' || rec_inner.empno || ', 姓名: ' || rec_inner.ename || ', 薪水: ' || rec_inner.sal ); END LOOP; -- 结束内层循环 END LOOP; -- 结束外层循环 END; / ``` 上述代码展示了两个层次的游标使用方法。`outer_cursor` 遍历 `dept` 表中的每一个部门号 (`deptno`);对于每个部门号,再利用 `inner_cursor` 查询该部门下的所有雇员信息(`empno`, `ename`, 和 `sal`)[^3]。 #### 关键点说明 - **游标声明位置**: 显式游标需先在 `DECLARE` 部分声明其 SQL 查询逻辑。 - **参数化游标**: 内层游标可以根据外部传递来的值动态调整查询条件 (如上例中的 `dept_number` 参数),从而达到灵活性的目的[^2]。 - **资源释放**: 尽管本例子采用的是隐式的 `FOR...LOOP` 形式无需手动控制打开和关闭动作,但在某些复杂场景下仍可能需要显式调用 `OPEN`, `FETCH`, 及 `CLOSE` 来管理资源占用情况。 #### 注意事项 如果存在大量数据或者深层嵌套,则可能会引起性能下降问题。因此实际开发过程中应尽量优化查询计划减少不必要的 I/O 开销以及考虑是否存在更高效的替代方案比如连接操作等。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值