Advertisement Space

Returning result sets from PL/SQL functions

Pipelined table functions let you write PL/SQL functions that return rows one at a time, which can then be queried with SELECT just like a regular table. They're perfect for data transformations, generating test data, or wrapping complex logic in a queryable interface.

Recipe #1 - Basic pipelined function

First, create a type for the rows you want to return:

CREATE OR REPLACE TYPE t_num_row AS OBJECT (
  id    NUMBER,
  value NUMBER
);

CREATE OR REPLACE TYPE t_num_table AS TABLE OF t_num_row;

Then create the pipelined function:

CREATE OR REPLACE FUNCTION generate_numbers(p_count NUMBER)
  RETURN t_num_table PIPELINED
AS
BEGIN
  FOR i IN 1..p_count LOOP
    PIPE ROW(t_num_row(i, DBMS_RANDOM.VALUE(1, 100)));
  END LOOP;
  RETURN;
END;

Now you can query it like a table:

SELECT * FROM TABLE(generate_numbers(10)) ORDER BY value DESC;

Recipe #2 - Transforming data with a pipelined function

A practical example that splits a delimited string into rows:

CREATE OR REPLACE TYPE t_varchar_row AS OBJECT (item VARCHAR2(4000));
CREATE OR REPLACE TYPE t_varchar_table AS TABLE OF t_varchar_row;

CREATE OR REPLACE FUNCTION split_string(
  p_string    VARCHAR2,
  p_delimiter VARCHAR2 DEFAULT ','
) RETURN t_varchar_table PIPELINED
AS
  l_str  VARCHAR2(32767) := p_string;
  l_pos  NUMBER;
BEGIN
  LOOP
    l_pos := INSTR(l_str, p_delimiter);
    IF l_pos > 0 THEN
      PIPE ROW(t_varchar_row(TRIM(SUBSTR(l_str, 1, l_pos - 1))));
      l_str := SUBSTR(l_str, l_pos + LENGTH(p_delimiter));
    ELSE
      PIPE ROW(t_varchar_row(TRIM(l_str)));
      EXIT;
    END IF;
  END LOOP;
  RETURN;
END;

Usage:

SELECT * FROM TABLE(split_string('Oracle,MySQL,PostgreSQL,SQLite'));

The key advantage of pipelined functions is that calling code starts receiving rows immediately — the function doesn't have to finish generating all rows before the first one is returned. This makes them very memory-efficient for large result sets.