Использование табличной функции Oracle из хранимой процедуры - PullRequest
0 голосов
/ 10 мая 2019

Я работаю с базой данных ERP, которая хранит много данных в многолетних таблицах. (Каждый новый год создается новая таблица для хранения данных за этот год) У нас есть требование иметь возможность запрашивать и составлять отчеты по некоторым из этих таблиц. Я в настоящее время использую представления, но представления становятся все больше и больше из года в год и медленнее. Я создал конвейерную табличную функцию, которая выполняет некоторый динамический sql и запрашивает соответствующие таблицы на основе даты и времени, переданной в качестве параметров. Я могу вызвать конвейерную функцию из обычного SQL, и она отлично работает. Однако цель состоит в том, чтобы иметь возможность повторно использовать табличные функции во многих различных хранимых процедурах и объединяться с другими данными. Используемая нами система отчетности требует использования хранимых процедур, возвращающих ссылки на курсоры.

Я создал тестовую функцию и протестировал хранимую процедуру (упрощенные версии для краткости), чтобы попытаться вернуть табличную функцию в качестве курсора, но я получаю ошибку при выполнении процедуры (PLS-00382: выражение имеет неправильный тип ). Я даже не уверен, можно ли получить доступ к функции конвейера с помощью процедуры, но я делал подобные вещи в SQL Server, поэтому должен быть какой-то способ. Я искал привет и низко, но на самом деле не могу найти кого-то с точно такой же ситуацией. Пожалуйста, смотрите мой код.

Ниже приведены пользовательские типы, которые я создал в своей схеме:

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, имеют много столбцов, поэтому я выбираю только первые 2, чтобы упростить задачу. Эти первые 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;

Кто-нибудь знает, возможно ли это с помощью хранимой процедуры? Возможно ли это с помощью нетранслируемых табличных функций? Будем благодарны за любые предложения или советы относительно лучшего метода достижения этого и поддержания производительности.

Вот полная ошибка, возвращаемая TOAD, когда я пытаюсь выполнить процедуру:

[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;

1 Ответ

0 голосов
/ 10 мая 2019

Вы можете переписать все это в одной процедуре, например:

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.

...