oracle 21c for循环核心是统一迭代器语法:reverse、by、values of、indices of等均为可组合、可赋值的迭代器结构,支持多迭代器笛卡尔积、when/while谓词过滤及mutable/immutable变量控制,但需注意空集合初始化、副作用表达式重求值等兼容性陷阱。
oracle 21c 的 for 循环确实更强大,核心在于它把原本分散在不同上下文(如限定表达式、for 循环、select ... from table())中的迭代逻辑,统一收束到一套可复用、可组合的 iterator 语法上。这不是简单加几个关键字,而是重构了 pl/sql 的迭代抽象层。
迭代器(iterator)不是新函数,是语法结构
很多人一看到 FOR i IN 1..10 就以为这只是循环写法,其实从 18c 开始,1..10 已被正式定义为一种 iterator —— 它本身可独立存在、可嵌套、可带修饰。21c 把这个概念彻底打通:iterator 可用于 FOR 循环,也可直接用在集合构造(如 TABLE(ITERATOR(...))),甚至能参与表达式推导。
-
REVERSE不再只是循环修饰符,而是iterator自身的属性,比如REVERSE 1..5是一个完整迭代器值,可赋给变量或传入函数 -
BY step支持负步长(10..1 BY -2),但注意:若上下限与步长符号冲突(如1..10 BY -1),运行时报错ORA-06502: PL/SQL: numeric or value error -
VALUES OF和INDICES OF这类迭代器必须作用于已初始化的集合,对空集合(COUNT = 0)直接跳过循环体,不报错也不执行
多迭代器并行声明(MUTABLE/IMMUTABLE 控制变量生命周期)
21c 允许在一个 FOR 头部声明多个迭代器,例如 FOR i IN 1..3, j IN REVERSE 10..12 LOOP。这时每个迭代变量默认是 MUTABLE(可修改),但如果你在循环体内意外重赋值(如 i := 5),下一轮迭代仍按原序列推进——i 的修改只在本轮有效,不影响迭代器本身的步进逻辑。
- 显式声明
i IMMUTABLE后,任何对i的赋值都会触发编译错误PLS-00363: expression 'i' cannot be used as an assignment target - 多个迭代器的组合产生的是笛卡尔积行为(类似嵌套循环),但底层是单层循环展开,性能优于手写两层
FOR - 若任一迭代器为空(如
INDICES OF empty_collection),整个多迭代器循环体完全不执行
WHILE 和 WHEN 提前终止不是“break”,是迭代器过滤
WHILE 和 WHEN 在 21c 中不是控制语句,而是迭代器的谓词子句(pred_clause_seq),它们在每次生成下一个值前就做判断,不满足即停止迭代——这意味着被跳过的值根本不会进入循环体,也不会触发 EXIT 或异常。
WHILE i :当 <code>i达到 5 时,迭代器直接结束,不执行本轮循环-
WHEN i != 3:跳过i = 3这个值,继续后续(如i = 4),相当于“过滤”而非“中断” - 两者可共存,但
WHILE优先级更高;若WHILE不满足,WHEN不会再检查 - 注意:这些谓词只作用于迭代器生成的值,无法访问循环体内的其他变量(如
WHEN total > 100是非法的)
容易被忽略的兼容性陷阱
21c 的迭代器增强在语法层面很优雅,但落地时有几个隐蔽坑点:
- 使用
VALUES OF或INDICES OF时,目标集合必须是已初始化的(IS NOT NULL),否则报ORA-06531: Reference to uninitialized collection,哪怕你写了IF l_col.COUNT > 0 THEN ... END IF也拦不住 -
REPEAT expr迭代器(重复某表达式 N 次)中,expr是每次循环都重新求值的,不是缓存一次;如果expr有副作用(如调用SEQ.NEXTVAL),就会被反复触发 - 在 SQL 上下文中(如
SELECT * FROM TABLE(ITERATOR(...))),21c 的迭代器语法不可用,只能用传统集合类型或PIPELINED函数替代
真正难的不是写出 FOR i IN REVERSE 1..100 BY -5 WHEN i > 20 这样的语句,而是理解它背后是一次性生成所有满足条件的索引序列,还是每次动态判断;以及当它嵌入复杂集合操作时,错误究竟来自迭代器定义,还是集合本身的生命周期管理。











