Parallelizing Table Functions. Oracle Release 2 (9.2)
Reklama
Reklama
Parallelizing Table Functions
Handling Exceptions in Table Functions
Exception handling in table functions works just as it does with ordinary user-defined functions.
Some languages, such as C and Java, provide a mechanism for user-supplied exception handling. If an exception raised within a table function is handled, the table function executes the exception handler and continues processing. Exiting the exception handler takes control to the enclosing scope. If the exception is cleared, execution proceeds normally.
An unhandled exception in a table function causes the parent transaction to roll back.
Parallelizing Table Functions
For a table function to be executed in parallel, it must have a partitioned input parameter. Parallelism is turned on for a table function if, and only if, both the following conditions are met:
■
■
The function has a
PARALLEL_ENABLE
clause in its declaration
Exactly one
REF CURSOR
argument is specified with a
PARTITION BY
clause
If the
PARTITION BY clause is not specified for any input
REF CURSOR as part of the
PARALLEL_ENABLE
clause, the SQL compiler cannot determine how to partition the data correctly. Note that only strongly typed
REF CURSOR arguments can be specified in the
PARTITION BY
clause, unless you use
PARTITION BY ANY
.
Parallel Execution of Table Functions
With parallel execution of a function that appears in the
SELECT
list, execution of the function is pushed down to and conducted by multiple slave scan processes.
These each execute the function on a segment of the function’s input data.
For example, the query
SELECT f(col1) FROM tab; is parallelized if f
is a pure function. The SQL executed by a slave scan process is similar to:
SELECT f(col1) FROM tab WHERE ROWID BETWEEN :b1 AND :b2;
PL/SQL Subprograms
8-43
Parallelizing Table Functions
Each slave scan operates on a range of rowids and applies function f
to each contained row. Function f
is then executed by the scan processes; it does not run independently of them.
Unlike a function that appears in the
SELECT
list, a table function is called in the
FROM
clause and returns a collection. This affects the way that table function input data is partitioned among slave scans because the partitioning approach must be appropriate for the operation that the table function performs. (For example, an
ORDER BY
operation requires input to be range-partitioned, whereas a
GROUP BY operation requires input to be hash partitioned.)
A table function itself specifies in its declaration the partitioning approach that is appropriate for it. (See
"Input Data Partitioning" on page 8-44.) The function is then
executed in a two-stage operation. First, one set of slave processes partitions the data as directed in the function’s declaration; then a second set of slave processes executes the table function in parallel on the partitioned data.
For example, the table function in the following query has a
REF CURSOR parameter:
SELECT * FROM TABLE(f(CURSOR(SELECT * FROM tab)));
The scan is performed by one set of slave processes, which redistributes the rows
(based on the partitioning method specified in the function declaration) to a second set of slave processes that actually executes function f
in parallel.
Input Data Partitioning
The table function declaration can specify data partitioning for exactly one
REF
CURSOR
parameter. The syntax to do this is as follows:
CREATE FUNCTION f(p ref cursor type) RETURN rec_tab_type PIPELINED
PARALLEL_ENABLE(PARTITION p BY [{HASH | RANGE} ( column list) | ANY ]) IS
BEGIN ... END;
The
PARTITION…BY
phrase in the
PARALLEL_ENABLE
clause specifies which one of the input cursors to partition and what columns to use for partitioning.
When explicit column names are specified in the column list, the partitioning method can be
RANGE
or
HASH
. The input rows will be hash- or range-partitioned on the columns specified.
The
ANY
keyword indicates that the function behavior is independent of the partitioning of the input data. When this keyword is used, the runtime system randomly partitions the data among the slaves. This keyword is appropriate for use
8-44
PL/SQL User’s Guide and Reference
Parallelizing Table Functions with functions that take in one row, manipulate its columns, and generate output row(s) based on the columns of this row only.
For example, the pivot-like function
StockPivot
shown below takes as input a row of the type:
(Ticker varchar(4), OpenPrice number, ClosePrice number) and generates rows of the type:
(Ticker varchar(4), PriceType varchar(1), Price number).
So the row (
"ORCL", 41, 42
) generates two rows (
"ORCL", "O", 41
) and
(
"ORCL", "C", 42
).
CREATE FUNCTION StockPivot(p refcur_pkg.refcur_t) RETURN rec_tab_type PIPELINED
PARALLEL_ENABLE(PARTITION p BY ANY) IS
ret_rec rec_type;
BEGIN
FOR rec IN p LOOP
ret_rec.Ticker := rec.Ticker;
ret_rec.PriceType := "O";
ret_rec.Price := rec.OpenPrice;
PIPE ROW(ret_rec);
ret_rec.Ticker := rec.Ticker; -- Redundant; not required
ret_rec.PriceType := "C";
ret_rec.Price := rec.ClosePrice;
push ret_rec;
END LOOP;
RETURN;
END;
The function f
can be used to generate another table from
Stocks
table in the following manner:
INSERT INTO AlternateStockTable
SELECT * FROM
TABLE(StockPivot(CURSOR(SELECT * FROM StockTable)));
If the
StockTable
is scanned in parallel and partitioned on
OpenPrice
, then the function
StockPivot
is combined with the data-flow operator doing the scan of
StockTable
and thus sees the same partitioning.
PL/SQL Subprograms
8-45
Parallelizing Table Functions
If, on the other hand, the
StockTable
is not partitioned, and the scan on it does not execute in parallel, the insert into
AlternateStockTable
also runs sequentially. Here is a slightly more complex example:
INSERT INTO AlternateStockTable
SELECT *
FROM TABLE(f(CURSOR(SELECT * FROM Stocks))),
TABLE(g(CURSOR( ... )))
WHERE join_condition; where g
is defined to be:
CREATE FUNCTION g(p refcur_pkg.refcur_t) RETURN ... PIPELINED
PARALLEL_ENABLE (PARTITION p BY ANY)
BEGIN ... END;
If function g
runs in parallel and is partitioned by
ANY
, then the parallel insert can belong in the same data-flow operator as g
.
Whenever the
ANY
keyword is specified, the data is partitioned randomly among the slaves. This effectively means that the function is executed in the same slave set which does the scan associated with the input parameter.
No redistribution or repartitioning of the data is required here. If the cursor p
itself is not parallelized, the incoming data is randomly partitioned on the columns in the column list. The round-robin table queue is used for this partitioning.
Parallel Execution of Leaf-level Table Functions
To use parallel execution with a function that produces multiple rows, but does not need to accept multiple rows as input and so does not require a
REF CURSOR
, arrange things so as to create a need for a
REF CURSOR
. That way, the function will have some way to partition the work.
For example, suppose that you want a function to read a set of external files in parallel and return the records they contain. To provide work for a
REF CURSOR
, you might first create a table and populate it with the filenames. A
REF CURSOR over this table can then be passed as a parameter to the table function
( readfiles
). The following code shows how this might be done:
CREATE TABLE filetab(filename VARCHAR(20));
INSERT INTO filetab VALUES('file0');
INSERT INTO filetab VALUES('file1');
...
INSERT INTO filetab VALUES('fileN');
8-46
PL/SQL User’s Guide and Reference
Preuzimanje
Reklama