Oracle 游标与 RECORD 类型详解
这是一个非常核心的 Oracle PL/SQL 知识点。让我从基础到实战,彻底讲清楚。
一、Oracle 读取游标的完整流程
在 Oracle 中,读取游标的流程与 SQL Server 类似,但语法不同:
text
┌─────────────────────────────────────────────────────────────┐ │ 步骤1:声明游标 → DECLARE CURSOR │ │ 步骤2:声明记录变量 → 用于接收每行数据 │ │ 步骤3:打开游标 → OPEN │ │ 步骤4:循环取数据 → LOOP + FETCH + EXIT WHEN │ │ 步骤5:关闭游标 → CLOSE │ └─────────────────────────────────────────────────────────────┘
二、完整代码示例
sql
DECLARE
-- 1. 声明游标
CURSOR cur_emp IS
SELECT empno, ename, sal
FROM emp
WHERE deptno = 20;
-- 2. 声明 RECORD 类型变量(用来存放一行数据)
v_emp cur_emp%ROWTYPE;
BEGIN
-- 3. 打开游标
OPEN cur_emp;
-- 4. 循环取数据
LOOP
FETCH cur_emp INTO v_emp;
EXIT WHEN cur_emp%NOTFOUND; -- 没有数据了就退出
-- 处理每一行数据
DBMS_OUTPUT.PUT_LINE('员工:' || v_emp.ename || ',工资:' || v_emp.sal);
END LOOP;
-- 5. 关闭游标
CLOSE cur_emp;END;/三、RECORD 类型变量详解
1. 什么是 RECORD?
RECORD(记录类型) 是 Oracle PL/SQL 中的一种复合数据类型,可以包含多个字段,就像一个临时的、只有一行的微型表。
2. 为什么需要 RECORD?
| 问题 | 没有 RECORD | 有 RECORD |
|---|---|---|
| 取一行数据 | 需要声明多个变量 | 一个变量就够了 |
| 代码长度 | 很长 | 很短 |
| 维护性 | 差(列变了要改多处) | 好(只改一处) |
没有 RECORD 的写法(繁琐):
sql
DECLARE CURSOR cur_emp IS SELECT empno, ename, sal FROM emp; v_empno emp.empno%TYPE; v_ename emp.ename%TYPE; v_sal emp.sal%TYPE;BEGIN OPEN cur_emp; LOOP FETCH cur_emp INTO v_empno, v_ename, v_sal; -- 三个变量 EXIT WHEN cur_emp%NOTFOUND; -- 使用 v_empno, v_ename, v_sal END LOOP; CLOSE cur_emp;END;
有 RECORD 的写法(简洁):
sql
DECLARE CURSOR cur_emp IS SELECT empno, ename, sal FROM emp; v_emp cur_emp%ROWTYPE; -- 一个变量搞定所有列BEGIN OPEN cur_emp; LOOP FETCH cur_emp INTO v_emp; -- 一个变量 EXIT WHEN cur_emp%NOTFOUND; -- 使用 v_emp.empno, v_emp.ename, v_emp.sal END LOOP; CLOSE cur_emp;END;
四、声明 RECORD 的三种方式
方式1:基于表或游标的 %ROWTYPE(最常用)
sql
-- 基于表v_emp emp%ROWTYPE; -- 字段和 emp 表完全一样-- 基于游标CURSOR cur_emp IS SELECT empno, ename FROM emp;v_emp cur_emp%ROWTYPE; -- 字段和游标选中的列一样
方式2:自定义 RECORD 类型
sql
DECLARE TYPE emp_record_type IS RECORD ( empno NUMBER, ename VARCHAR2(50), sal NUMBER ); v_emp emp_record_type;BEGIN SELECT empno, ename, sal INTO v_emp FROM emp WHERE rownum = 1; DBMS_OUTPUT.PUT_LINE(v_emp.ename);END;
方式3:混合使用
sql
DECLARE CURSOR cur_emp IS SELECT empno, ename FROM emp; v_emp cur_emp%ROWTYPE; -- v_emp 只有 empno 和 ename 两个字段BEGIN OPEN cur_emp; FETCH cur_emp INTO v_emp; CLOSE cur_emp; DBMS_OUTPUT.PUT_LINE(v_emp.empno || ' - ' || v_emp.ename);END;
五、Oracle 读取游标的三种循环方式
方式1:LOOP...END LOOP(最灵活)
sql
DECLARE CURSOR cur_emp IS SELECT empno, ename FROM emp; v_emp cur_emp%ROWTYPE;BEGIN OPEN cur_emp; LOOP FETCH cur_emp INTO v_emp; EXIT WHEN cur_emp%NOTFOUND; -- 处理数据 END LOOP; CLOSE cur_emp;END;
方式2:WHILE 循环
sql
DECLARE CURSOR cur_emp IS SELECT empno, ename FROM emp; v_emp cur_emp%ROWTYPE;BEGIN OPEN cur_emp; FETCH cur_emp INTO v_emp; WHILE cur_emp%FOUND LOOP -- 处理数据 FETCH cur_emp INTO v_emp; END LOOP; CLOSE cur_emp;END;
方式3:FOR 循环(最简洁,推荐)
sql
DECLARE CURSOR cur_emp IS SELECT empno, ename FROM emp;BEGIN FOR v_emp IN cur_emp LOOP -- Oracle 自动打开、取数据、关闭游标 -- v_emp 是隐式声明的 RECORD 变量 DBMS_OUTPUT.PUT_LINE(v_emp.empno || ' - ' || v_emp.ename); END LOOP;END;
六、%ROWTYPE vs %TYPE 对比
属性 | %TYPE | %ROWTYPE |
作用 | 取单个列的数据类型 | 取整行的数据类型 |
声明 | v_name emp.ename%TYPE | v_emp emp%ROWTYPE |
存储内容 | 单个值 | 整行数据(多个值) |
访问方式 | 直接使用变量名 | v_emp.ename(点号访问) |
sql
DECLARE v_name emp.ename%TYPE; -- 只能存一个名字 v_emp emp%ROWTYPE; -- 可以存 empno, ename, sal, hiredate...BEGIN SELECT ename INTO v_name FROM emp WHERE rownum = 1; SELECT * INTO v_emp FROM emp WHERE rownum = 1; DBMS_OUTPUT.PUT_LINE(v_name); -- 直接使用 DBMS_OUTPUT.PUT_LINE(v_emp.ename); -- 点号访问END;/
七、游标属性详解
| 属性 | 含义 | 返回值 |
|---|---|---|
%FOUND | 最近一次 FETCH 是否取到数据 | TRUE / FALSE |
%NOTFOUND | 最近一次 FETCH 是否没取到数据 | TRUE / FALSE |
%ROWCOUNT | 已取出的行数 | 数字 |
%ISOPEN | 游标是否已打开 | TRUE / FALSE |
sql
DECLARE
CURSOR cur_emp IS SELECT ename FROM emp;
v_ename emp.ename%TYPE;BEGIN
OPEN cur_emp;
LOOP
FETCH cur_emp INTO v_ename;
EXIT WHEN cur_emp%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('第 ' || cur_emp%ROWCOUNT || ' 行:' || v_ename);
END LOOP;
CLOSE cur_emp;END;/八、词源角度
| 单词 | 词源 | 含义 |
|---|---|---|
| RECORD | 拉丁语 recordari(记住、回想) | 记录、档案 |
| ROW | 古英语 rāw | 行 |
| TYPE | 拉丁语 typus(模型、类型) | 类型 |
| CURSOR | 拉丁语 cursor(奔跑者) | 游标 |
%ROWTYPE 的字面意思:"和某表或游标的一行数据同样的类型"
九、总结对比表
对比项 | SQL Server | Oracle |
接收数据的变量 | 声明多个 @变量 | 用 %ROWTYPE 一个变量搞定 |
取数据语法 | FETCH INTO @v1, @v2 | FETCH INTO v_emp |
循环判断 | WHILE @@FETCH_STATUS = 0 | EXIT WHEN cursor%NOTFOUND |
最简方式 | 没有类似 FOR 循环 | FOR v_emp IN cursor LOOP |
十、一句话总结
RECORD 类型的变量就像一张只有一行的微型表,可以存储从游标或表中取出的一整行数据。
用 %ROWTYPE 声明 RECORD 变量后,你可以:
用
FETCH cursor INTO v_record一次性接收整行数据用
v_record.列名访问其中的某个字段代码更简洁、更易维护
Oracle 读取游标最推荐的方式:FOR v_emp IN cursor LOOP,它会自动处理开、取、关,你只需要专注处理业务逻辑