Oracle PL/SQL 游标:隐式、显式、For 循环示例

⚡ 智能摘要

光标 Oracle PL/SQL 游标是指向上下文区域的指针,该上下文区域保存着 SQL 语句返回的行。游标有两种类型:隐式游标,为 DML 自动创建;以及显式游标,由程序员声明和控制。

  • 📍 背景区域: 游标指向存储 SQL 语句及其返回的活动集的上下文区域。
  • ⚙️ 隐式光标: Oracle 对于每个 DML 语句和单行 SELECT INTO 语句,都会自动打开一个隐式游标。
  • 显式光标: 程序员通过声明、打开、获取和关闭显式游标来实现完全控制。
  • 🔎 光标属性: %FOUND、%NOTFOUND、%ISOPEN 和 %ROWCOUNT 报告最近一次操作的状态。
  • 🔁 游标 FOR 循环: FOR 循环会自动打开、获取和关闭游标,无需手动操作。
  • 🤖 人工智能协助: GitHub Copilot 等 AI 助手会草拟光标循环并标记未关闭的光标。

Oracle PL/SQL 游标隐式、显式和 FOR 循环

PL/SQL 中的 CURSOR 是什么?

光标是指向上下文区域的指针。 Oracle 创建用于处理上下文区域的 SQL 声明,此区域包含有关该声明的所有信息。

PL / SQL 允许程序员通过游标控制上下文区域。游标保存 SQL 语句返回的行,游标保存的行集合称为活动集。这些游标也可以命名,以便在代码的其他位置引用它们。

光标有两种类型:

  • 隐式光标
  • 显式光标

隐式光标

无论何时 DML操作 当数据库中发生操作时,会创建一个隐式游标来保存该操作影响的行。这些游标不能命名,因此无法在代码的其他位置进行控制或引用。我们只能通过游标属性来引用最近创建的游标。

显式光标

程序员可以创建命名上下文区域来执行 DML 操作,并对其进行更多控制。显式游标应在声明部分中定义。 PL/SQL 块它是为代码中需要使用的 SELECT 语句创建的。

以下是使用显式游标的步骤:

  • 声明游标: 声明游标实际上就是为声明部分定义的 SELECT 语句创建一个命名的上下文区域。该上下文区域的名称与游标名称相同。
  • 打开光标: 打开游标会指示 PL/SQL 为该游标分配内存,并使游标准备好提取记录。
  • 从游标中获取数据: 在此过程中,执行 SELECT 语句,并将获取的行存储在已分配的内存中。这些行现在被称为活动集。从游标中获取数据是记录级操作,这意味着我们可以逐条记录地访问数据。每个 fetch 语句都会获取一个活动集,并保存该特定记录的信息。此语句与获取记录并将其赋值给 INTO 子句中变量的 SELECT 语句相同,但它不会抛出任何异常。 例外.
  • 关闭光标: 一旦所有记录都被获取完毕,我们需要关闭游标,以便释放分配给此上下文区域的内存。

句法

DECLARE
CURSOR <cursor_name> IS <SELECT statement>;
<cursor_variable declaration>;
BEGIN
OPEN <cursor_name>;
FETCH <cursor_name> INTO <cursor_variable>;
.
.
CLOSE <cursor_name>;
END;

在上述语法中,声明部分包含游标的声明以及用于存储提取数据的游标变量。游标是为游标声明中指定的 SELECT 语句创建的。在执行部分,声明的游标会被打开、提取数据并关闭。

游标属性

隐式游标和显式游标都具有一些可访问的属性。这些属性提供了有关游标操作的更多信息。以下列出了不同的游标属性及其用法。

游标属性 描述
%成立 如果最近一次获取操作成功获取了记录,则返回布尔结果 TRUE;否则返回 FALSE。
%未找到 与 %FOUND 的作用相反。如果最近一次获取操作未能获取到任何记录,则返回 TRUE。
%开了 如果给定的光标已打开,则返回布尔结果 TRUE;否则返回 FALSE。
%行数 返回一个数值,表示操作影响或获取的实际记录数。

显式游标示例: 在本示例中,我们将学习如何声明、打开、获取和关闭显式游标。我们将使用游标从 emp 表中获取所有员工姓名。我们还将使用游标属性来设置循环,使其从游标中获取所有记录。

下面的屏幕截图显示了此显式光标示例及其输出。 Oracle.

使用显式游标从 emp 表中获取员工姓名 Oracle PL / SQL

DECLARE
CURSOR guru99_det IS SELECT emp_name FROM emp;
lv_emp_name emp.emp_name%type;
BEGIN
OPEN guru99_det;
LOOP
FETCH guru99_det INTO lv_emp_name;
IF guru99_det%NOTFOUND
THEN
EXIT;
END IF;
Dbms_output.put_line('Employee Fetched:'||lv_emp_name);
END LOOP;
Dbms_output.put_line('Total rows fetched is'||guru99_det%ROWCOUNT);
CLOSE guru99_det;
END;
/

输出

Employee Fetched:BBB
Employee Fetched:XXX
Employee Fetched:YYY
Total rows fetched is 3

Code 说明

  • Code 第2行: 声明语句“SELECT emp_name FROM emp”的游标 guru99_det。
  • Code 第3行: 声明变量 lv_emp_name %类型 锚定到 emp.emp_name。
  • Code 第5行: 打开光标 guru99_det。
  • Code 第6行: 设置基本循环语句以获取 emp 表中的所有记录。
  • Code 第7行: 获取 guru99_det 数据并将值赋给 lv_emp_name。
  • Code 第8行: 使用游标属性 %NOTFOUND 检查游标中的所有记录是否都已获取。如果已获取,则返回 TRUE,控制流退出循环;否则,控制流将继续从游标中获取数据并打印出来。
  • Code 第10行: 循环语句的 EXIT 条件。
  • Code 第12行: 打印获取的员工姓名。
  • Code 第14行: 使用游标属性 %ROWCOUNT 查找游标获取的记录总数。
  • Code 第15行: 循环结束后,光标关闭,已分配的内存被释放。

FOR 循环游标语句

光标 FOR循环 可用于处理游标。我们可以在 FOR 循环语句中指定游标名称而不是范围限制,这样循环就会从游标的第一个记录执行到最后一个记录。游标变量的创建、游标的打开、游标的读取和游标的关闭都由 FOR 循环隐式完成。

句法

DECLARE
CURSOR <cursor_name> IS <SELECT statement>;
BEGIN
FOR I IN <cursor_name>
LOOP
.
.
END LOOP;
END;

在上述语法中,声明部分包含游标的声明。该游标是为游标声明中指定的 SELECT 语句创建的。在执行部分,声明的游标被放置在 FOR 循环中,此时循环变量 'I' 充当游标变量。

Oracle 游标 for 循环示例: 在这个例子中,我们将使用游标 FOR 循环从 emp 表中投影出所有员工姓名。

DECLARE
CURSOR guru99_det IS SELECT emp_name FROM emp;
BEGIN
FOR lv_emp_name IN guru99_det
LOOP
Dbms_output.put_line('Employee Fetched:'||lv_emp_name.emp_name);
END LOOP;
END;
/

输出

Employee Fetched:BBB
Employee Fetched:XXX
Employee Fetched:YYY

Code 说明

  • Code 第2行: 声明语句“SELECT emp_name FROM emp”的游标 guru99_det。
  • Code 第4行: 使用循环变量 lv_emp_name 为游标构建 FOR 循环。
  • Code 第6行: 在循环的每次迭代中打印员工姓名。
  • Code 第7行: 退出循环(结束循环)。

注意: 在游标 FOR 循环中,不能使用游标属性,因为游标的打开、获取和关闭是由 FOR 循环隐式完成的。

常见问题

REF CURSOR(游标变量)是指向查询结果集的指针。与静态游标不同,它可以在运行时打开不同的查询,并在 PL/SQL 代码块之间或与客户端程序之间传递结果。

普通游标每次 FETCH 操作提取一行,导致频繁的上下文切换。 批量收集 一次获取即可将多行数据加载到集合中,从而大幅降低大型结果集的开销。

是的。声明一个参数化游标,例如 CURSOR c(dept NUMBER) IS SELECT …,然后在 OPEN c(10) 中传递值。参数允许您使用不同的筛选值重用同一个游标定义。

FOR UPDATE 会锁定游标选中的行,防止其他人修改。WHERE CURRENT OF 则会更新或删除刚刚获取的行,而不会重复 WHERE 条件。

打开的游标会占用内存,并计入 OPEN_CURSORS 限制。如果打开的游标过多,最终会导致 ORA-01000 错误:超出最大打开游标数量限制,因此使用后务必显式关闭游标。

每次 FETCH 操作都会在 PL/SQL 和 SQL 引擎之间切换。成千上万次这样的上下文切换累积起来,因此,通常使用单个基于集合的 SQL 语句或 BULK COLLECT 语句处理相同的行要快得多。

是的。 GitHub 副驾驶 它会根据注释草拟明确的 OPEN、FETCH 和 CLOSE 循环或游标 FOR 循环,添加 %NOTFOUND 退出检查,并建议属性名称,但您应该先查看逻辑。

AI 助手会标记出逐行游标循环(这些循环可能会转化为基于集合的 SQL 或 BULK COLLECT 语句),识别未关闭的游标,并解释 %attribute 的行为。这种机器学习审查能够在代码投入生产环境之前提升性能。

总结一下这篇文章: