Можно ли передавать курсоры с параметром в качестве параметра процедуры? - PullRequest
2 голосов
/ 06 февраля 2020

У меня много подобных процедур, и я хочу сделать сценарий pl / sql для этого. Это легче понять, посмотрев на код, чем объяснив его, поэтому здесь это упрощенная версия:

create or replace package my_test as
  cursor my_cursor(my_filter NUMBER) is select column1, column2 from mytable where column3 = my_filter;
  cursor my_cursor2(my_filter VARCHAR2) is select column1, column2 from mytable where column4 LIKE my_filter;
  -- ...

  procedure test1;
  procedure test2(p_cursor XXX);
  function test3(test_record XXX%ROWTYPE) return number;

end my_test;
/

create or replace package body my_test as
  procedure test1 is    
  begin
    test2(my_cursor(3));
    test2(my_cursor2('foo%'));
    test2(my_cursor(5));
    -- ...
  end test1;

  procedure test2(p_cursor XXX) is
    tmp number;
  begin
    for r in p_cursor loop      
      --some actions 
      tmp := test3(r);
      --some actions
    end loop;
  end test2;

  function test3(test_record XXX%ROWTYPE) return number is
    tmp_sum number;
  begin
    -- ...
    tmp_sum := test_record.column1 + test_record.column2;
    -- ...
    return l_summ;
  end test3;


end my_test;
/

BEGIN
    my_test.test1();
END;
/

Я старался изо всех сил, но не смог. Может быть, кто-то может мне помочь? Можно ли добиться такой вещи? Что я должен поставить вместо XXX?

Ответы [ 2 ]

2 голосов
/ 06 февраля 2020

Определите тип записи, чтобы соответствовать проекции запросов, которые вы хотите выполнить. Вы можете использовать это для определения параметра IN test3().

Определить курсор ref, который возвращает этот тип записи. Вы можете использовать это для определения параметра IN test2().

. Вместо курсоров задайте функции, которые возвращают ref-курсоры, определенные этим типом курсора.

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

create or replace package my_test as

  type t_record is record(
     column1 mytable.column1%type
    ,column2 mytable.column2%type
  );

  type t_cursor is ref cursor return t_record;

  function my_cursor (my_filter NUMBER)   return t_cursor ;
  function my_cursor2(my_filter VARCHAR2) return t_cursor ;

  procedure test1;
  procedure test2(p_cursor t_cursor);
  function  test3(test_record t_record) return number;

end my_test;
/

, а реализация выглядит так (с некоторым выводом, чтобы сделать демонстрацию более понятной):

create or replace package body my_test as

  function my_cursor(my_filter NUMBER) return t_cursor is 
      rc sys_refcursor;
  begin
    open rc for  select column1, column2 from mytable where column3 = my_filter;
    return rc;
  end my_cursor;

  function my_cursor2(my_filter VARCHAR2)  return t_cursor is 
      rc sys_refcursor;
  begin
    open rc for  select column1, column2 from mytable where column4 LIKE my_filter;
    return rc;
  end my_cursor2;

  procedure test1 is    
  begin
    dbms_output.put_line('test2(my_cursor, 3)');
    test2(my_cursor(3)); 
    dbms_output.put_line('test2(my_cursor2, foo');
    test2(my_cursor2('foo%'));
    dbms_output.put_line('test2(my_cursor,5)');
    test2(my_cursor(5));
    -- ...
  end test1;

  procedure test2(p_cursor t_cursor) is
    tmp number;
    l_rec t_record;
  begin
    loop
      fetch p_cursor into l_rec;
      exit when p_cursor%notfound;
      --some actions 
      tmp := test3(l_rec);
      dbms_output.put_line(l_rec.column1 ||'+'||l_rec.column2||'='||tmp);
      --some actions
    end loop;
    close p_cursor;
  end test2;

  function test3(test_record t_record) return number is
    tmp_sum number;
  begin
    -- ...
    tmp_sum := test_record.column1 + test_record.column2;
    -- ...
    return tmp_sum;
  end test3;

end my_test;
/

Сделал Я говорю демо? Конечно, демо на db <> fiddle .

0 голосов
/ 06 февраля 2020

Переменные курсора идеально подходят для того, что вы хотите сделать!

Вот переписать часть вашего кода, иллюстрирующую, как их использовать:

CREATE OR REPLACE PACKAGE my_test
AS
   PROCEDURE test1;

   PROCEDURE test2 (p_cursor IN OUT SYS_REFCURSOR);
END my_test;
/

CREATE OR REPLACE PACKAGE BODY my_test
AS
   FUNCTION my_cursor (my_filter NUMBER)
      RETURN SYS_REFCURSOR
   IS
      l_cursor   SYS_REFCURSOR;
   BEGIN
      OPEN l_cursor FOR
         SELECT column1, column2
           FROM mytable
          WHERE column3 = my_filter;

      RETURN l_cursor;
   END;

   PROCEDURE test1
   IS
   BEGIN
      test2 (my_cursor (3));
      test2 (my_cursor (5));
   END test1;

   PROCEDURE test2 (p_cursor xxx)
   IS
      tmp   NUMBER;
   BEGIN
      LOOP
         FETCH p_cursor INTO tmp;

         EXIT WHEN p_cursor%NOTFOUND;
         --some actions
         NULL;
      END LOOP;

      CLOSE p_cursor;
   END test2;
END my_test;
/

Вы используете OPEN FOR синтаксис, чтобы связать запрос (и его набор результатов) с переменной. Затем вы можете использовать обычные операции FETCH, CLOSE, ссылки на атрибуты курсора%.

И закройте курсор, когда закончите. : -)

...