Writing Oracle stored procedure with Oracle table as Input parameter

Viewed 529

How to write Oracle stored procedure with a table (X) as input parameter and that table X is used inside procedure to join with another table Y?

Table X will have thousands of records.

Not looking to pass table name as varchar and then using dynamic SQL (so, this option is out of picture)

3 Answers

From 19.6 you can create a SQL macro. This returns a string with your query fragment.

At parse time the database will do a find/replace of the table parameter with the table you've passed it:

create or replace function f ( tab dbms_tf.table_t ) 
  return varchar2 sql_macro as
begin
  return 'select * from tab 
    join ( select level rn from dual connect by level <= 2 ) 
    on c1 = rn';
end f;
/

create table t1 (
  c1 int
);
create table t2 (
  c1 int
);

insert into t1 values ( 1 );
insert into t2 values ( 2 );


select * from f ( t1 );

C1       RN   
    1     1 

select * from f ( t2 );

C1       RN   
    2     2 

There's another approach you might find interesting: pass a cursor variable to pipelined table function, invoke it in SQL, allowing you literally pass the contents of the table (select * from...), bulk collect into collection, then join the collection with your other table!

DROP TYPE tickertype FORCE;
DROP TYPE tickertypeset FORCE;
DROP TABLE stocktable;
DROP TABLE tickertable;

CREATE TABLE stocktable
(
   ticker        VARCHAR2 (20),
   trade_date    DATE,
   open_price    NUMBER,
   close_price   NUMBER
)
/

BEGIN
   FOR indx IN 1 .. 100
   LOOP
      INSERT INTO stocktable
           VALUES ('STK' || indx,
                   SYSDATE,
                   indx,
                   indx + 15);
   END LOOP;

   COMMIT;
END;
/

CREATE TABLE tickertable
(
   ticker      VARCHAR2 (20),
   pricedate   DATE,
   pricetype   VARCHAR2 (1),
   price       NUMBER
)
/

CREATE TYPE tickertype AS OBJECT
(
   ticker VARCHAR2 (20),
   pricedate DATE,
   pricetype VARCHAR2 (1),
   price NUMBER
);
/

BEGIN
   FOR indx IN 1 .. 100
   LOOP
      INSERT INTO tickertable
           VALUES ('STK' || indx,
                   SYSDATE,
                   'O',
                   indx);
   END LOOP;

   COMMIT;
END;
/

CREATE TYPE tickertypeset AS TABLE OF tickertype;
/

CREATE OR REPLACE PACKAGE refcur_pkg
   AUTHID DEFINER
IS
   TYPE refcur_t IS REF CURSOR
      RETURN stocktable%ROWTYPE;

   TYPE dataset_tt IS TABLE OF stocktable%ROWTYPE;
END refcur_pkg;
/

CREATE OR REPLACE FUNCTION pipeliner (dataset refcur_pkg.refcur_t)
   RETURN tickertypeset
   PIPELINED
   AUTHID DEFINER
IS
   l_row_as_object   tickertype
                        := tickertype (NULL,
                                       NULL,
                                       NULL,
                                       NULL);

   l_dataset         refcur_pkg.dataset_tt;
   l_count             PLS_INTEGER;
BEGIN
   FETCH dataset BULK COLLECT INTO l_dataset;

   CLOSE dataset;

   /* Let's do a join with another table. */
   SELECT COUNT (*) into l_count
     FROM TABLE (l_dataset) st, tickertable tt
    WHERE st.ticker = tt.ticker;

   DBMS_OUTPUT.put_line ('Count = ' ||l_count);

   l_row_as_object.ticker := 'ABC';
   PIPE ROW (l_row_as_object);

   RETURN;
END;
/

BEGIN
   FOR rec
      IN (SELECT * FROM TABLE (pipeliner (CURSOR (SELECT * FROM stocktable))))
   LOOP
      DBMS_OUTPUT.put_line (rec.ticker);
   END LOOP;
END;
/

I see this output:

Count = 100
ABC

Create a table type in the SQL scope:

CREATE TYPE string_list AS TABLE OF VARCHAR2(5);

Then use that as the parameter for your stored procedure and join it to another table using a Table Collection Expression:

CREATE PROCEDURE test_proc(
  p_list IN string_list
)
IS
  v_cursor SYS_REFCURSOR;
  v_string VARCHAR2(10);
BEGIN
  OPEN v_cursor FOR
  SELECT d.*
  FROM   DUAL d
         INNER JOIN TABLE( p_list ) t
         ON ( d.DUMMY = t.COLUMN_VALUE );

  -- do something with the cursor.
 LOOP 
   FETCH v_cursor into v_string;
   EXIT WHEN v_cursor%NOTFOUND;
   DBMS_OUTPUT.PUT_LINE( v_string );
 END LOOP;
END;
/

Then you can call it:

BEGIN
  test_proc( string_list( 'X', 'Y', 'Z' ) ) ;
END;
/

and it outputs:

X

db<>fiddle here

Related