从存储过程中使用 Oracle Table 函数
Consuming Oracle Table Function from Stored Procedure
我正在使用一个 ERP 数据库,该数据库存储了多年 table 中的大量数据。 (每个新年都会创建一个新的 table 来保存那一年的数据)我们需要能够查询和报告其中一些 table。我目前正在使用视图,但这些视图年复一年地变得越来越大,而且速度越来越慢。我创建了一个流水线 table 函数,该函数执行一些动态 sql 并根据作为参数传入的起始日期和截止日期查询适当的 table。我可以从普通 SQL 调用流水线函数,它工作正常。但是,目标是能够在许多不同的存储过程中重复使用 table 函数并与其他数据连接。我们使用的报告系统要求我们使用存储过程return引用游标。
我创建了一个测试函数和测试存储过程(为简洁起见的简化版本)以尝试 return table 函数作为游标,但我在执行该过程时遇到错误(PLS-00382:表达式类型错误)。我什至不确定过程是否可以访问管道功能,但我在 SQL 服务器中做过类似的事情,所以必须有某种方式。我搜索过高低,但找不到任何人有完全相同的情况。请看我的代码。
以下是我在模式中创建的用户定义类型:
CREATE OR REPLACE TYPE PUCCONNECT.wo_trans_type AS OBJECT
(GL_YEAR INT,
SUBSYSTEM VARCHAR2(2)
);
CREATE OR REPLACE TYPE PUCCONNECT.wo_trans_table_test AS TABLE OF PUCCONNECT.wo_trans_type;
以下是函数和过程声明。我在 GL101Txx 函数中选择的 tables 有很多列,所以我只选择前 2 列以保持简单。前两列与我的用户定义对象 "wo_trans_type "
中定义的列具有相同的定义
CREATE OR REPLACE FUNCTION PUCCONNECT.WO_MULTIYEAR_TEST(fromdate date, todate date)
RETURN WO_TRANS_TABLE_TEST PIPELINED IS
TYPE ref0 IS REF CURSOR;
cur0 ref0;
v_year_start int;
v_year_end int;
out_rec wo_trans_type
:= wo_trans_type(NULL,NULL);
BEGIN
v_year_start := EXTRACT(year FROM fromdate);
v_year_end := EXTRACT(year FROM todate);
FOR yearNumber in v_year_start..v_year_end LOOP
OPEN cur0 FOR
'SELECT ' || yearNumber || ' "gl_year", GL.SUBSYSTEM
FROM fmsdata.GL101T' || SUBSTR(to_char(yearNumber), 3,2) || ' GL
WHERE (GL.transaction_date BETWEEN ''' || fromdate || ''' AND ''' || todate || ''')';
LOOP
FETCH cur0 INTO out_rec.gl_year, out_rec.subsystem;
EXIT WHEN cur0 %NOTFOUND;
PIPE ROW(out_rec);
END LOOP;
CLOSE cur0;
END LOOP;
RETURN;
END WO_MULTIYEAR_TEST;
这是我尝试使用函数的过程:
CREATE OR REPLACE PROCEDURE PUCCONNECT."SP_WO_TRANS_PA" (
--table_out out wo_trans_table,
wo_trans_cursor out sys_refcursor
)
AS
BEGIN
OPEN wo_trans_cursor FOR
SELECT gl_year, subsystem
FROM TABLE( PUCCONNECT.WO_MULTIYEAR_TEST('01-jan-2019', '05-may-2019'));
END;
有谁知道这是否可以通过存储过程实现?非流水线 table 函数是否可行?对实现此目标和保持性能的最佳方法的任何建议或意见表示赞赏。
这是我尝试执行过程时 TOAD 给出的完整错误 return:
[Error] ORA-06550: line 12, column 12:
PLS-00382: expression is of wrong type
ORA-06550: line 12, column 6:
PL/SQL: Statement ignored
(1: 0): >> DECLARE
-- Declarations
l_WO_TRANS_CURSOR SYS_REFCURSOR;
BEGIN
-- Call
PUCCONNECT.SP_WO_TRANS_PA (WO_TRANS_CURSOR => l_WO_TRANS_CURSOR);
-- Transaction Control
COMMIT;
-- Output values, do not modify
:1 := l_WO_TRANS_CURSOR;
END;
Error at line 1
ORA-06550: line 12, column 12:
PLS-00382: expression is of wrong type
ORA-06550: line 12, column 6:
PL/SQL: Statement ignored
这是我在 TOAD 中的调用方式:
DECLARE
-- Declarations
l_WO_TRANS_CURSOR SYS_REFCURSOR;
BEGIN
-- Call
PUCCONNECT.SP_WO_TRANS_PA (WO_TRANS_CURSOR => l_WO_TRANS_CURSOR);
-- Transaction Control
COMMIT;
-- Output values, do not modify
:1 := l_WO_TRANS_CURSOR;
END;
你可以在一个过程中重写整个事情,比如:
CREATE OR REPLACE PROCEDURE PUCCONNECT."SP_WO_TRANS_PA"(fromdate IN DATE,
todate IN DATE,
wo_trans_cursor OUT SYS_REFCURSOR) AS
v_year_start INT;
v_year_end INT;
v_sql CLOB;
c_union_all CONSTANT VARCHAR2(9) := 'union all';
v_table_append VARCHAR2(38);
v_column_append VARCHAR2(10);
BEGIN
v_year_start := extract(YEAR FROM fromdate);
v_year_end := extract(YEAR FROM todate);
FOR yearnumber IN v_year_start .. v_year_end
LOOP
IF yearnumber != v_year_start
THEN
v_sql := v_sql || chr(10) || c_union_all || chr(10) ||;
END IF;
-- to avoid any sql injection due to the to_char (just in case)
v_table_append := dbms_assert.qualified_sql_name('fmsdata.GL101T' || substr(to_char(yearnumber), 3, 2));
v_column_append := dbms_assert.enquote_literal(to_char(yearnumber));
v_sql := v_sql || 'select to_number(' || v_column_append || ') gl_year, gl.subsystem ' || chr(10)
|| 'from v_table_append' || chr(10)
|| 'where gl.transaction_date between :fromdate and :todate';
END LOOP;
v_sql := v_sql || chr(10) || 'order by 1, 2';
OPEN wo_trans_cursor FOR v_sql
USING fromdate, todate;
END sp_wo_trans_pa;
/
这遍历年份并在游标中生成 sql 到 运行,然后使用相关绑定变量打开游标。
我在可能的情况下使用绑定变量,在不可能的情况下,dbms_assert 清理连接的值以避免任何 SQL 注入漏洞。
我正在使用一个 ERP 数据库,该数据库存储了多年 table 中的大量数据。 (每个新年都会创建一个新的 table 来保存那一年的数据)我们需要能够查询和报告其中一些 table。我目前正在使用视图,但这些视图年复一年地变得越来越大,而且速度越来越慢。我创建了一个流水线 table 函数,该函数执行一些动态 sql 并根据作为参数传入的起始日期和截止日期查询适当的 table。我可以从普通 SQL 调用流水线函数,它工作正常。但是,目标是能够在许多不同的存储过程中重复使用 table 函数并与其他数据连接。我们使用的报告系统要求我们使用存储过程return引用游标。
我创建了一个测试函数和测试存储过程(为简洁起见的简化版本)以尝试 return table 函数作为游标,但我在执行该过程时遇到错误(PLS-00382:表达式类型错误)。我什至不确定过程是否可以访问管道功能,但我在 SQL 服务器中做过类似的事情,所以必须有某种方式。我搜索过高低,但找不到任何人有完全相同的情况。请看我的代码。
以下是我在模式中创建的用户定义类型:
CREATE OR REPLACE TYPE PUCCONNECT.wo_trans_type AS OBJECT
(GL_YEAR INT,
SUBSYSTEM VARCHAR2(2)
);
CREATE OR REPLACE TYPE PUCCONNECT.wo_trans_table_test AS TABLE OF PUCCONNECT.wo_trans_type;
以下是函数和过程声明。我在 GL101Txx 函数中选择的 tables 有很多列,所以我只选择前 2 列以保持简单。前两列与我的用户定义对象 "wo_trans_type "
中定义的列具有相同的定义CREATE OR REPLACE FUNCTION PUCCONNECT.WO_MULTIYEAR_TEST(fromdate date, todate date)
RETURN WO_TRANS_TABLE_TEST PIPELINED IS
TYPE ref0 IS REF CURSOR;
cur0 ref0;
v_year_start int;
v_year_end int;
out_rec wo_trans_type
:= wo_trans_type(NULL,NULL);
BEGIN
v_year_start := EXTRACT(year FROM fromdate);
v_year_end := EXTRACT(year FROM todate);
FOR yearNumber in v_year_start..v_year_end LOOP
OPEN cur0 FOR
'SELECT ' || yearNumber || ' "gl_year", GL.SUBSYSTEM
FROM fmsdata.GL101T' || SUBSTR(to_char(yearNumber), 3,2) || ' GL
WHERE (GL.transaction_date BETWEEN ''' || fromdate || ''' AND ''' || todate || ''')';
LOOP
FETCH cur0 INTO out_rec.gl_year, out_rec.subsystem;
EXIT WHEN cur0 %NOTFOUND;
PIPE ROW(out_rec);
END LOOP;
CLOSE cur0;
END LOOP;
RETURN;
END WO_MULTIYEAR_TEST;
这是我尝试使用函数的过程:
CREATE OR REPLACE PROCEDURE PUCCONNECT."SP_WO_TRANS_PA" (
--table_out out wo_trans_table,
wo_trans_cursor out sys_refcursor
)
AS
BEGIN
OPEN wo_trans_cursor FOR
SELECT gl_year, subsystem
FROM TABLE( PUCCONNECT.WO_MULTIYEAR_TEST('01-jan-2019', '05-may-2019'));
END;
有谁知道这是否可以通过存储过程实现?非流水线 table 函数是否可行?对实现此目标和保持性能的最佳方法的任何建议或意见表示赞赏。
这是我尝试执行过程时 TOAD 给出的完整错误 return:
[Error] ORA-06550: line 12, column 12:
PLS-00382: expression is of wrong type
ORA-06550: line 12, column 6:
PL/SQL: Statement ignored
(1: 0): >> DECLARE
-- Declarations
l_WO_TRANS_CURSOR SYS_REFCURSOR;
BEGIN
-- Call
PUCCONNECT.SP_WO_TRANS_PA (WO_TRANS_CURSOR => l_WO_TRANS_CURSOR);
-- Transaction Control
COMMIT;
-- Output values, do not modify
:1 := l_WO_TRANS_CURSOR;
END;
Error at line 1
ORA-06550: line 12, column 12:
PLS-00382: expression is of wrong type
ORA-06550: line 12, column 6:
PL/SQL: Statement ignored
这是我在 TOAD 中的调用方式:
DECLARE
-- Declarations
l_WO_TRANS_CURSOR SYS_REFCURSOR;
BEGIN
-- Call
PUCCONNECT.SP_WO_TRANS_PA (WO_TRANS_CURSOR => l_WO_TRANS_CURSOR);
-- Transaction Control
COMMIT;
-- Output values, do not modify
:1 := l_WO_TRANS_CURSOR;
END;
你可以在一个过程中重写整个事情,比如:
CREATE OR REPLACE PROCEDURE PUCCONNECT."SP_WO_TRANS_PA"(fromdate IN DATE,
todate IN DATE,
wo_trans_cursor OUT SYS_REFCURSOR) AS
v_year_start INT;
v_year_end INT;
v_sql CLOB;
c_union_all CONSTANT VARCHAR2(9) := 'union all';
v_table_append VARCHAR2(38);
v_column_append VARCHAR2(10);
BEGIN
v_year_start := extract(YEAR FROM fromdate);
v_year_end := extract(YEAR FROM todate);
FOR yearnumber IN v_year_start .. v_year_end
LOOP
IF yearnumber != v_year_start
THEN
v_sql := v_sql || chr(10) || c_union_all || chr(10) ||;
END IF;
-- to avoid any sql injection due to the to_char (just in case)
v_table_append := dbms_assert.qualified_sql_name('fmsdata.GL101T' || substr(to_char(yearnumber), 3, 2));
v_column_append := dbms_assert.enquote_literal(to_char(yearnumber));
v_sql := v_sql || 'select to_number(' || v_column_append || ') gl_year, gl.subsystem ' || chr(10)
|| 'from v_table_append' || chr(10)
|| 'where gl.transaction_date between :fromdate and :todate';
END LOOP;
v_sql := v_sql || chr(10) || 'order by 1, 2';
OPEN wo_trans_cursor FOR v_sql
USING fromdate, todate;
END sp_wo_trans_pa;
/
这遍历年份并在游标中生成 sql 到 运行,然后使用相关绑定变量打开游标。
我在可能的情况下使用绑定变量,在不可能的情况下,dbms_assert 清理连接的值以避免任何 SQL 注入漏洞。