一、游标基础概念
游标(Cursor)是 Oracle 中用于逐行处理 SELECT 查询结果集的数据库对象。可以把它想象成一个 “指针”,这个指针会指向结果集中的某一行,允许你一次只处理一行数据,而不是一次性处理整个结果集,这在处理大量数据或需要逐行判断 / 操作时非常有用。
-- 1. 声明游标
DECLARE
  -- 定义游标,可带参数
  CURSOR 游标名 [(参数名 数据类型)] IS
    SELECT 列1, 列2 FROM 表名 WHERE 条件;
  -- 定义变量接收游标每行数据(字段数/类型需与游标查询结果匹配)
  变量1 数据类型;
  变量2 数据类型;
BEGIN
  -- 2. 打开游标(执行查询,生成结果集)
  OPEN 游标名 [(参数值)];
  
  -- 3. 读取游标(逐行获取数据)
  LOOP
    FETCH 游标名 INTO 变量1, 变量2; -- 将当前行数据存入变量
    EXIT WHEN 游标名%NOTFOUND; -- 无数据时退出循环(核心终止条件)
    
    -- 业务逻辑:处理当前行数据
    DBMS_OUTPUT.PUT_LINE('列1值:' || 变量1 || ',列2值:' || 变量2);
  END LOOP;
  
  -- 4. 关闭游标(释放资源)
  CLOSE 游标名;
EXCEPTION
  WHEN OTHERS THEN
    -- 异常处理:确保游标关闭
    IF 游标名%ISOPEN THEN
      CLOSE 游标名;
    END IF;
    DBMS_OUTPUT.PUT_LINE('错误:' || SQLERRM);
END;

示例:

SET SERVEROUTPUT ON; -- 开启输出(SQL*Plus/PL/SQL Developer需执行)

DECLARE
  -- 声明游标:查询部门10的员工
  CURSOR emp_cursor IS
    SELECT empno, ename, sal FROM emp WHERE deptno = 10;
  -- 定义变量接收游标数据
  v_empno emp.empno%TYPE;
  v_ename emp.ename%TYPE;
  v_sal   emp.sal%TYPE;
BEGIN
  -- 打开游标
  OPEN emp_cursor;
  
  -- 循环读取数据
  LOOP
    FETCH emp_cursor INTO v_empno, v_ename, v_sal;
    EXIT WHEN emp_cursor%NOTFOUND; -- 无数据时退出
    
    -- 输出当前行数据
    DBMS_OUTPUT.PUT_LINE('员工编号:' || v_empno || ',姓名:' || v_ename || ',工资:' || v_sal);
  END LOOP;
  
  -- 关闭游标
  CLOSE emp_cursor;
EXCEPTION
  WHEN OTHERS THEN
    IF emp_cursor%ISOPEN THEN
      CLOSE emp_cursor;
    END IF;
    DBMS_OUTPUT.PUT_LINE('执行错误:' || SQLERRM);
END;

如果需要动态指定查询条件(如不同部门),可以给游标加参数:

SET SERVEROUTPUT ON;

DECLARE
  -- 声明带参数的游标:参数为部门编号
  CURSOR emp_cursor(p_deptno emp.deptno%TYPE) IS
    SELECT empno, ename, sal FROM emp WHERE deptno = p_deptno;
  v_empno emp.empno%TYPE;
  v_ename emp.ename%TYPE;
  v_sal   emp.sal%TYPE;
BEGIN
  -- 打开游标并传入参数(查询部门20)
  OPEN emp_cursor(20);
  
  LOOP
    FETCH emp_cursor INTO v_empno, v_ename, v_sal;
    EXIT WHEN emp_cursor%NOTFOUND;
    
    DBMS_OUTPUT.PUT_LINE('员工编号:' || v_empno || ',姓名:' || v_ename || ',工资:' || v_sal);
  END LOOP;
  
  CLOSE emp_cursor;
END;

Oracle 提供了 FOR 循环游标语法,无需手动OPEN/FETCH/CLOSE,Oracle 会自动管理游标生命周期,代码更简洁:

SET SERVEROUTPUT ON;

DECLARE
  -- 声明游标(也可直接写在FOR循环中)
  CURSOR emp_cursor(p_deptno emp.deptno%TYPE) IS
    SELECT empno, ename, sal FROM emp WHERE deptno = p_deptno;
BEGIN
  -- FOR循环自动遍历游标,row为游标行记录(Oracle自动定义)
  FOR emp_row IN emp_cursor(30) LOOP
    DBMS_OUTPUT.PUT_LINE('员工编号:' || emp_row.empno || ',姓名:' || emp_row.ename || ',工资:' || emp_row.sal);
  END LOOP;
  -- 无需关闭游标,FOR循环结束自动关闭
END;

-- 更简化:直接在FOR循环中写查询语句(无需提前声明游标)
DECLARE
BEGIN
  FOR emp_row IN (SELECT empno, ename, sal FROM emp WHERE deptno = 30) LOOP
    DBMS_OUTPUT.PUT_LINE('员工编号:' || emp_row.empno || ',姓名:' || emp_row.ename || ',工资:' || emp_row.sal);
  END LOOP;
END;

游标属性用于判断游标状态,是使用游标的关键,常用属性如下:
%ISOPEN 判断游标是否已打开(TRUE/FALSE)
%FOUND 上一次 FETCH 是否获取到数据(TRUE/FALSE)
%NOTFOUND 上一次 FETCH 是否未获取到数据(TRUE/FALSE)
%ROWCOUNT 已从游标中读取的行数

示例:使用%ROWCOUNT统计读取行数

SET SERVEROUTPUT ON;

DECLARE
  CURSOR emp_cursor IS SELECT * FROM emp WHERE deptno = 10;
  emp_row emp%ROWTYPE; -- 使用表%ROWTYPE接收整行数据
BEGIN
  OPEN emp_cursor;
  LOOP
    FETCH emp_cursor INTO emp_row;
    EXIT WHEN emp_cursor%NOTFOUND;
    DBMS_OUTPUT.PUT_LINE('已读取' || emp_cursor%ROWCOUNT || '行,员工姓名:' || emp_row.ename);
  END LOOP;
  CLOSE emp_cursor;
END;

隐式游标(Oracle 自动管理)隐式游标主要用于 DML 语句(INSERT/UPDATE/DELETE)或 SELECT INTO,Oracle 会自动创建名为SQL的隐式游标,通过SQL%属性判断执行结果:

SET SERVEROUTPUT ON;

BEGIN
  -- 更新员工工资
  UPDATE emp SET sal = sal * 1.1 WHERE empno = 7369;
  
  -- 通过隐式游标判断更新结果
  IF SQL%FOUND THEN
    DBMS_OUTPUT.PUT_LINE('更新成功,影响行数:' || SQL%ROWCOUNT);
  ELSE
    DBMS_OUTPUT.PUT_LINE('未找到该员工,更新失败');
  END IF;
  
  COMMIT;
EXCEPTION
  WHEN OTHERS THEN
    ROLLBACK;
    DBMS_OUTPUT.PUT_LINE('更新错误:' || SQLERRM);
END;

总结游标核心作用:逐行处理 SELECT 结果集,适用于需要逐行判断 / 操作数据的场景。
显式游标使用步骤:声明 → 打开 → 读取(FETCH + 循环) → 关闭;推荐使用 FOR 循环游标(自动管理生命周期,代码更简洁)。
关键属性:%NOTFOUND(循环终止条件)、%ROWCOUNT(统计行数)、%FOUND(判断是否找到数据)是最常用的游标属性。
使用游标时需注意:处理完数据后务必关闭游标(尤其是显式游标),避免占用数据库资源;对于大量数据,游标逐行处理效率较低,可优先考虑批量操作(如 BULK COLLECT)。

posted on 2026-01-16 16:59  邢帅杰  阅读(4)  评论(0)    收藏  举报