Home > Back-end >  Cursor as in parameter - refactoring a procedure
Cursor as in parameter - refactoring a procedure

Time:10-02

I have many functions similiar to this one:

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
PROCEDURE REP_HELPER (myIdx IN BINARY_INTEGER, from_d IN DATE, rep_table IN OUT rep_table_T) IS
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------



CURSOR myCUR IS SELECT myField1,
                        myField2,
                        myField3,
                        myField4,
                        myField5,
                        myField6,
                        myField7,
                        myField8,
                        myField9,
                        myField10,
                        myField11,
                        myField12,
                        myField13,
                        myField14,
                        myField15,
                        myField16,
                        myField17,
                        myField18,
                        myField19,
                        myField20,
                        myField21,
                        myField22,
                        myField23,
                        myField24,
                        myField25,
                        myField26,
                        myField27,
                        myField28,
                        myField29,
                        myField30,
                        myField31
                   FROM myTable;
BEGIN 
    -- I wish to move the part below to different procedure
    OPEN myCUR;
    FETCH myCUR INTO rep_table(myIdx).day1,  rep_table(myIdx).day2,  rep_table(myIdx).day3,  rep_table(myIdx).day4,  rep_table(myIdx).day5,
                      rep_table(myIdx).day6,  rep_table(myIdx).day7,  rep_table(myIdx).day8,  rep_table(myIdx).day9,  rep_table(myIdx).day10,
                      rep_table(myIdx).day11, rep_table(myIdx).day12, rep_table(myIdx).day13, rep_table(myIdx).day14, rep_table(myIdx).day15,
                      rep_table(myIdx).day16, rep_table(myIdx).day17, rep_table(myIdx).day18, rep_table(myIdx).day19, rep_table(myIdx).day20,
                      rep_table(myIdx).day21, rep_table(myIdx).day22, rep_table(myIdx).day23, rep_table(myIdx).day24, rep_table(myIdx).day25,
                      rep_table(myIdx).day26, rep_table(myIdx).day27, rep_table(myIdx).day28, rep_table(myIdx).day29, rep_table(myIdx).day30,
                      rep_table(myIdx).day31;
    CLOSE myCUR;
END  REP_HELPER; 

I wish to do the part from open myCUR; to close myCUR; in a separate procedure. As I have many functions like the above but the cursor is always different. So I would like to have procedure which would do the open,fetch, close part :

PROCEDURE PB_HELPER_READ_INTO_DAYS(nIndex IN BINARY_INTEGER, myCUR by reference, rep_table IN OUT rep_table_T)

Is it possible to to it in plsql?

CodePudding user response:

Option which would work is to create a package, declare cursor globally and use it in any procedure you want. For example:

SQL> create or replace package pkg_test is
  2    procedure p1;
  3  end;
  4  /

Package created.

SQL> create or replace package body pkg_test is
  2    cursor c1 is select * from dept;
  3    c1r c1%rowtype;
  4
  5
  6    procedure p1 is
  7    begin
  8      open  c1;
  9      fetch c1 into c1r;
 10      close c1;
 11    end p1;
 12  end;
 13  /

Package body created.

SQL>

This won't work: declaring a cursor in one procedure and working with it in another:

SQL> create or replace package pkg_test is
  2    procedure p1;
  3    procedure p2;
  4  end;
  5  /

Package created.

SQL> create or replace package body pkg_test is
  2    procedure p1 is
  3      cursor c1 is select * from dept;
  4      c1r c1%rowtype;
  5    begin
  6      null;
  7    end p1;
  8
  9
 10    procedure p2 is
 11    begin
 12      open  c1;
 13      fetch c1 into p1.c1r;
 14      close c1;
 15    end p2;
 16  end;
 17  /

Warning: Package Body created with compilation errors.

SQL> show err
Errors for PACKAGE BODY PKG_TEST:

LINE/COL ERROR
-------- -----------------------------------------------------------------
12/5     PL/SQL: SQL Statement ignored
12/11    PLS-00201: identifier 'C1' must be declared
13/5     PL/SQL: SQL Statement ignored
13/11    PLS-00201: identifier 'C1' must be declared
14/5     PL/SQL: SQL Statement ignored
14/11    PLS-00201: identifier 'C1' must be declared
SQL>

Also, you can't reference it using the "owner" procedure's prefix:

SQL> create or replace package body pkg_test is
  2    procedure p1 is
  3      cursor c1 is select * from dept;
  4      c1r c1%rowtype;
  5    begin
  6      null;
  7    end p1;
  8
  9
 10    procedure p2 is
 11    begin
 12      open  p1.c1;
 13      fetch p1.c1 into p1.c1r;
 14      close p1.c1;
 15    end p2;
 16  end;
 17  /

Warning: Package Body created with compilation errors.

SQL> show err
Errors for PACKAGE BODY PKG_TEST:

LINE/COL ERROR
-------- -----------------------------------------------------------------
12/5     PL/SQL: SQL Statement ignored
12/14    PLS-00225: subprogram or cursor 'P1' reference is out of scope
13/5     PL/SQL: SQL Statement ignored
13/11    PLS-00225: subprogram or cursor 'P1' reference is out of scope
14/5     PL/SQL: SQL Statement ignored
14/11    PLS-00225: subprogram or cursor 'P1' reference is out of scope
SQL>

CodePudding user response:

You can put only FETCH and CLOSE in a different procedure. Would be this (when you have only one OUT parameter, then I prefer a FUNCTION):

CREATE OR REPLACE FUNCTION REP_HELPER (myIdx IN BINARY_INTEGER, from_d IN DATE) RETURN SYS_REFCURSOR IS    
   myCur SYS_REFCURSOR;    
BEGIN

   OPEN myCur FOR
   SELECT myField1, ...
   FROM myTable;
   RETURN myCur;

END REP_HELPER; 

And use it like this:

DECLARE
    cur SYS_REFCURSOR;
BEGIN 
    
    cur := REP_HELPER(...);    
    FETCH cur INTO ...

    CLOSE cur;
END;

A more advanced solution would be dynamic SQL with DBMS_SQL Package:

CREATE OR REPLACE FUNCTION REP_HELPER(myIdx IN BINARY_INTEGER, from_d IN DATE) RETURN NUMBER IS

  curid NUMBER := DBMS_SQL.OPEN_CURSOR;
  sql_stmt VARCHAR2(32000);
BEGIN

  sql_stmt := 'SELECT myField1, ... FROM myTable';
  DBMS_SQL.PARSE(curid, sql_stmt, DBMS_SQL.NATIVE);
  RETURN  curid;

END REP_HELPER;

DECLARE
    cur SYS_REFCURSOR;
    curid NUMBER;
    ret INTEGER;
BEGIN 
    
    curid := REP_HELPER(...);    
    ret := DBMS_SQL.EXECUTE(curid);
   -- Switch from DBMS_SQL to native dynamic SQL
    cur := DBMS_SQL.TO_REFCURSOR(curid);
    FETCH cur INTO ...

    CLOSE cur;
END;

But I think this would be an overkill.

  • Related