2

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)

5
  • How your end result should look alike? Commented Sep 30, 2020 at 14:34
  • 1
    how you will join to another table without knowing which column needs to be joined? Commented Sep 30, 2020 at 14:40
  • You can assume any column, suppose ID, to join. I know the column to join. Its just I am not able to use input table in a join query in the procedure. Commented Sep 30, 2020 at 14:49
  • What is your oracle version? Is it 18+? Commented Sep 30, 2020 at 14:54
  • 2
    @VikashKumarSharma "Not looking to pass table name as varchar" so how do you want to declare your input parameters? Show us wanted procedure declaration Commented Sep 30, 2020 at 14:56

3 Answers 3

3

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 
Sign up to request clarification or add additional context in comments.

Comments

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

Comments

0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.