一、游标基础概念
游标(Cursor)是 Oracle 中用于逐行处理 SELECT 查询结果集的数据库对象。可以把它想象成一个 “指针”,这个指针会指向结果集中的某一行,允许你一次只处理一行数据,而不是一次性处理整个结果集,这在处理大量数据或需要逐行判断 / 操作时非常有用。
游标(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)。
浙公网安备 33010602011771号