Oracle PL/SQL PIPELINED 表函数详解

Oracle PL/SQL PIPELINED 表函数详解

适用版本:Oracle Database 10g / 11g / 12c / 19c / 23ai 文档版本:v1.0 / 2026-07


1. 概述

PIPELINED 表函数流式返回结果[1]:

详细见:Oracle PL/SQL 表函数


2. 基本

2.1 类型

CREATE OR REPLACE TYPE emp_obj AS OBJECT (
  id NUMBER,
  name VARCHAR2(100),
  salary NUMBER
);
/

CREATE OR REPLACE TYPE emp_tab AS TABLE OF emp_obj;
/

2.2 函数

CREATE OR REPLACE FUNCTION get_emp(p_dept_id NUMBER) 
  RETURN emp_tab PIPELINED
IS
  v_emp emp_obj;
BEGIN
  FOR rec IN (SELECT id, name, salary FROM employees WHERE dept_id = p_dept_id) LOOP
    PIPE ROW(emp_obj(rec.id, rec.name, rec.salary));
  END LOOP;
  
  RETURN;
END;
/

2.3 查询

SELECT * FROM TABLE(get_emp(10));

SELECT * FROM TABLE(get_emp(10)) WHERE salary > 5000;

3. PARALLEL_ENABLE

CREATE OR REPLACE FUNCTION get_emp_parallel(p_dept_id NUMBER)
  RETURN emp_tab
  PARALLEL_ENABLE (PARTITION cur BY ANY)
  PIPELINED
IS
BEGIN
  FOR rec IN (SELECT ... ) LOOP
    PIPE ROW(...);
  END LOOP;
  RETURN;
END;
/

4. 流式处理

4.1 CURSOR 参数

CREATE OR REPLACE FUNCTION transform_emp(p_cur SYS_REFCURSOR)
  RETURN emp_tab PIPELINED
IS
  v_id NUMBER;
  v_name VARCHAR2(100);
  v_salary NUMBER;
BEGIN
  LOOP
    FETCH p_cur INTO v_id, v_name, v_salary;
    EXIT WHEN p_cur%NOTFOUND;
    
    -- 转换
    v_salary := v_salary * 1.1;
    
    PIPE ROW(emp_obj(v_id, v_name, v_salary));
  END LOOP;
  
  CLOSE p_cur;
  RETURN;
END;
/

-- 使用
SELECT * FROM TABLE(transform_emp(
  CURSOR(SELECT id, name, salary FROM employees)
));

4.2 链式

SELECT * FROM TABLE(transform_emp(
  CURSOR(SELECT * FROM TABLE(filter_emp(...)))
));

5. 聚合

5.1 输出多行

CREATE OR REPLACE FUNCTION split_string(p_str VARCHAR2, p_sep VARCHAR2 := ',')
  RETURN SYS.ODCIVARCHAR2LIST PIPELINED
IS
  v_idx NUMBER;
  v_str VARCHAR2(4000) := p_str;
BEGIN
  LOOP
    v_idx := INSTR(v_str, p_sep);
    IF v_idx > 0 THEN
      PIPE ROW(SUBSTR(v_str, 1, v_idx - 1));
      v_str := SUBSTR(v_str, v_idx + 1);
    ELSE
      PIPE ROW(v_str);
      EXIT;
    END IF;
  END LOOP;
  RETURN;
END;
/

-- 使用
SELECT * FROM TABLE(split_string('a,b,c,d'));

6. 异常处理

CREATE OR REPLACE FUNCTION safe_get_emp(p_dept NUMBER)
  RETURN emp_tab PIPELINED
IS
BEGIN
  FOR rec IN (SELECT ... WHERE dept_id = p_dept) LOOP
    PIPE ROW(...);
  END LOOP;
  RETURN;
EXCEPTION
  WHEN OTHERS THEN
    -- 必须 PIPE 或 RETURN
    RETURN;
END;
/

7. 性能

7.1 流式

- 不需全部加载
- 内存友好
- 实时

7.2 PARALLEL_ENABLE

- 并行
- 性能
- 大数据

7.3 vs 普通

- PIPELINED:流式
- 普通:全部内存

详细见:Oracle PL/SQL 性能优化详解


8. 应用场景

8.1 数据转换

CREATE OR REPLACE FUNCTION transform_data(p_src SYS_REFCURSOR)
  RETURN result_tab PIPELINED
IS
  ...
BEGIN
  LOOP
    FETCH p_src INTO ...;
    -- 转换
    PIPE ROW(...);
  END LOOP;
END;
/

INSERT INTO target SELECT * FROM TABLE(transform_data(
  CURSOR(SELECT * FROM source)
));

8.2 文件解析

CREATE OR REPLACE FUNCTION parse_file(p_dir VARCHAR2, p_file VARCHAR2)
  RETURN data_tab PIPELINED
IS
  v_file UTL_FILE.FILE_TYPE;
  v_line VARCHAR2(4000);
BEGIN
  v_file := UTL_FILE.FOPEN(p_dir, p_file, 'R');
  LOOP
    UTL_FILE.GET_LINE(v_file, v_line);
    -- 解析
    PIPE ROW(...);
  END LOOP;
EXCEPTION
  WHEN NO_DATA_FOUND THEN
    UTL_FILE.FCLOSE(v_file);
    RETURN;
END;
/

SELECT * FROM TABLE(parse_file('DATA_DIR', 'data.csv'));

8.3 字符串拆分

-- 已示例
SELECT t.column_value 
FROM TABLE(split_string('a,b,c')) t;

8.4 Web Service

CREATE OR REPLACE FUNCTION get_weather(p_city VARCHAR2)
  RETURN weather_tab PIPELINED
IS
  v_response VARCHAR2(4000);
BEGIN
  v_response := UTL_HTTP.REQUEST('http://api.weather.com/' || p_city);
  -- 解析 JSON
  PIPE ROW(...);
  RETURN;
END;
/

9. ANY 类型

9.1 系统类型

-- SYS.ODCIVARCHAR2LIST
-- SYS.ODCINUMBERLIST
-- SYS.ODCIDATELIST

CREATE OR REPLACE FUNCTION get_ids(p_dept NUMBER)
  RETURN SYS.ODCINUMBERLIST PIPELINED
IS
BEGIN
  FOR rec IN (SELECT id FROM employees WHERE dept_id = p_dept) LOOP
    PIPE ROW(rec.id);
  END LOOP;
  RETURN;
END;
/

SELECT * FROM TABLE(get_ids(10));

10. 多类型

10.1 多行多列

CREATE OR REPLACE TYPE sale_rec AS OBJECT (
  sale_date DATE,
  amount NUMBER,
  region VARCHAR2(50)
);
/

CREATE OR REPLACE TYPE sale_tab AS TABLE OF sale_rec;
/

CREATE OR REPLACE FUNCTION generate_sales(p_year NUMBER)
  RETURN sale_tab PIPELINED
IS
BEGIN
  FOR m IN 1..12 LOOP
    FOR r IN ('East', 'West', 'Central') LOOP
      PIPE ROW(sale_rec(
        TO_DATE(p_year || '-' || m || '-01', 'YYYY-MM-DD'),
        DBMS_RANDOM.VALUE(1000, 10000),
        r
      ));
    END LOOP;
  END LOOP;
  RETURN;
END;
/

SELECT * FROM TABLE(generate_sales(2025));

11. 与普通表函数

PIPELINED普通
内存流式全部
实时
性能大数据小数据
复杂

12. 常见坑与排错

12.1 ORA-06512

- PIPE ROW 类型不匹配
- 检查

12.2 内存

- 普通函数全部内存
- PIPELINED 流式

12.3 并行

- PARALLEL_ENABLE
- CURSOR 参数

13. 最佳实践

  1. PIPELINED:流式
  2. PARALLEL_ENABLE:并行
  3. CURSOR 参数:链式
  4. 类型:明确
  5. 异常处理:完整
  6. RETURN:结束
  7. ANY 类型:简化
  8. 测试:验证
  9. 性能:大数据
  10. 文档化:说明

14. 参考资料

[1] Oracle Database PL/SQL Language Reference 19c, “Pipelined Table Functions” https://docs.oracle.com/en/database/oracle/oracle-database/19/lnpls/plsql-optimization-and-tuning.html