简体   繁体   中英

Oracle 12c: Can I invoke a function from a WITH clause which both takes and returns a table?

I want to write a PL/SQL function which can be used in a variety of queries, particular the subqueries of a WITH clause. The tricky part is that I want the function to both receive and return a one-column TABLE (or CURSOR) of information.

Details: imagine that this function just sorts a list of employee IDs according to some very complicated criteria. We'd have this:

  • My function is called SORT_EMPLOYEES
  • My function takes a 1-column table of employee IDs (emp_id) as input.
  • This input type is probably a TABLE type EMP_T_TYPE.
  • This return type is probably also a TABLE type EMP_T_TYPE.

So far so good, I hope.

Now, I think that I can use the output of the function with the TABLE operator pretty much anywhere; eg:

    WITH
    wanted_emps AS (...),  -- find employees we want, as table of emp_id
    ranked_emps AS (
        SELECT rownum() as rank, emp_id 
        FROM TABLE(SORT_EMPLOYEES(...???...))
    ),
    ...

The problem is: how can I get the list of employees from 'wanted_emps' and make it the input of SORT_EMPLOYEES? What goes in the "...???..." above? Is this even possible?

Please note that I want this to be used from plain SQL, especially from subqueries of WITH clauses as shown above -- not from PL/SQL. Thanks!

Here is an example of how to COLLECT values into a collection which can then be passed to a FUNCTION that returns another collection:

Oracle Setup :

CREATE TYPE numbers_table AS TABLE OF NUMBER;

CREATE TABLE test_data ( grp, value ) AS
  SELECT 1, 1 FROM DUAL UNION ALL
  SELECT 1, 2 FROM DUAL UNION ALL
  SELECT 1, 3 FROM DUAL UNION ALL
  SELECT 2, 4 FROM DUAL UNION ALL
  SELECT 2, 5 FROM DUAL UNION ALL
  SELECT 3, 6 FROM DUAL;

Query :

WITH FUNCTION square( i_numbers IN numbers_table ) RETURN numbers_table
IS
  p_numbers numbers_table := numbers_table();
  p_count   PLS_INTEGER;
BEGIN
  IF i_numbers IS NULL THEN
    p_count := 0;
  ELSE
    p_count := i_numbers.COUNT;
  END IF;
  p_numbers.EXTEND( p_count );
  FOR i IN 1 .. p_count LOOP
    p_numbers(i) := i_numbers(i) * i_numbers(i);
  END LOOP;
  RETURN p_numbers;
END;
collected_rows ( grp, grouped_values ) AS (
  SELECT grp,
         CAST(
           COLLECT( value ORDER BY value )
           AS numbers_table
         )
  FROM   test_data
  GROUP BY grp
)
SELECT c.grp,
       t.COLUMN_VALUE AS squared_value
FROM   collected_rows c
       CROSS JOIN
       TABLE( square( c.grouped_values ) ) t;

Output :

\nGRP |  SQUARED_VALUE \n--: |  ------------: \n  1 |  1 \n  1 |  4 \n  1 |  9 \n  2 |  16 \n  2 |  25 \n  3 |  36 \n

db<>fiddle here

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM