管道化表函数(Pipelined Table Function)在 Oracle 19c 中并非“开箱即用就能获得高性能”——必须严格满足四项关键约束,才能真正发挥流式输出与并行执行的优势。否则,实际性能甚至可能比普通函数更差。

必须先创建集合类型,不能直接使用系统内置类型
Oracle 规定 PIPELINED 函数的返回值必须是显式声明的集合类型(TABLE OF)。切勿图省事直接使用 SYS.ODCIVARCHAR2LIST 或 SYS.ODCINUMBERLIST 这类系统类型——虽然编译可能通过,但调用时极易遇到 ORA-22905 错误,或导致优化器基数估算严重偏离实际(默认猜测为 8168 行,与实际数据量级相差巨大)。
- 先创建对象类型:
CREATE OR REPLACE TYPE t_row AS OBJECT (id NUMBER, name VARCHAR2(100)) - 再创建表类型:
CREATE OR REPLACE TYPE t_rows AS TABLE OF t_row - 函数签名必须为:
FUNCTION f_transform(p_limit IN NUMBER) RETURN t_rows PIPELINED
函数体内只能使用 PIPE ROW(),不能返回 RETURN 值
函数内部禁止包含任何带值的 RETURN 语句,也不允许先构造完整集合再一次性返回(例如 RETURN t_rows(...))。一旦出现这类写法,编译阶段会直接报错 PLS-00713(未声明为 PIPELINED 却使用了 PIPE ROW),或运行时触发 ORA-14551(无法在查询中执行 DML)。
- 正确做法:循环中每处理一行数据,就调用
PIPE ROW(t_row(...)) - 错误做法:
RETURN t_rows(...)、RETURN后跟空括号、RETURN NULL - 函数末尾必须保留一个无参数的
RETURN(不带任何值),否则 PL/SQL 无法通过编译
调用时必须嵌套 TABLE(),且参数位置不能出错
直接写 f_transform(1000) 属于非法操作——优化器无法识别该返回值结构,会立即报错 ORA-22905。必须使用 TABLE(f_transform(1000)) 进行包裹,且参数需放在括号内紧贴函数名。
- ✅ 正确:
SELECT * FROM TABLE(f_transform(1000)) - ❌ 错误:
SELECT * FROM TABLE(f_transform)(1000)(语法不合法) - ❌ 错误:
SELECT * FROM f_transform(1000)(触发ORA-22905) - 如果函数参数为
REF CURSOR,需确保上游游标稳定打开,避免被优化器提前物化
要启用并行必须双管齐下:PARALLEL_ENABLE + SQL 提示
PARALLEL_ENABLE 并非装饰性关键字,它与 SQL 层的并行提示是硬性联动关系。只配置其中一个而未配套使用另一个,函数仍然会以单线程方式运行。
- 函数声明必须包含:
FUNCTION f_split(...) RETURN t_rows PIPELINED PARALLEL_ENABLE - SQL 调用必须添加提示:
SELECT /*+ PARALLEL(4) */ * FROM TABLE(f_split(...)) - 函数体内严禁使用包变量、会话级临时表、
DBMS_SESSION.SET_IDENTIFIER等状态绑定操作,否则并行执行时会导致数据交叉污染,结果出现错乱 - 如需记录日志或写入控制表,必须使用
PRAGMA AUTONOMOUS_TRANSACTION封装 DML 操作
真正制约吞吐量的瓶颈,往往不在于数据量本身,而是类型未正确导出、TABLE() 遗漏未套、并行开关只开启了一半,或者函数内部偷偷使用了会话变量——这些细节一旦出错,流式处理就会退化为全量构造集合,性能反而比普通函数更加糟糕。
