Er is een stelling: 'If you can do it in SQL, use SQL.' Maar soms is zelfs de zeer krachtige Oracle versie van SQL niet genoeg en heb je behoefte aan méér, zoals loops, condities etc.
Oracle biedt sinds Oracle 9i de mogelijkheid om PL/SQL-code te bouwen en op te nemen in de FROM clause van je query. Hoe? Door de output van een PL/SQL functie zo te formatteren dat die op een tabel lijkt, dus met records van waarden (rijen met kolommen). Hiermee heb je alle kracht van PL/SQL én SQL tot je beschikking in je SQL-statement.
Deze aanpak biedt nóg een voordeel: de code in de PL/SQL-functie wordt slechts éénmaal uitgevoerd, en niet voor elke rij (functie in de WHERE-clause) of voor elke rij in het resultaat (functie in de SELECT).
If you can do it in SQL, use SQL
2. About me…
• Patrick Barel
• Working with Oracle since 1997
• Working with PL/SQL since 1999
• Playing with APEX since 2003 (mod_plsql)
• ACE since 2011
• OCA since December 20th 2012
10. Table functions are functions that produce a
collection of rows (either a nested table or a
varray) that can be queried like a physical
database table.You use a table function like the
name of a database table, in the FROM clause of
a query.
Overview of table functions
11. Define Record Type
Define NestedTable type
Define function returning NestedTable type
Query function using the TABLE() operator
Table function concepts
12. • Defined as an object table of a collection
– First an object type
SQL> CREATE TYPE now_t AS OBJECT
2 (now VARCHAR2(8));
3 /
Type created.
Table function concepts, result set
13. • Defined as an object table of a collection
– Second; the collection type
SQL> CREATE TYPE now_tt AS
2 TABLE OF now_t;
3 /
Type created.
Table function concepts, result set
14. • Return type is the object table
CREATE OR REPLACE FUNCTION now
RETURN now_tt AS
l_returnvalue now_tt := now_tt();
…
BEGIN
…
RETURN l_returnvalue;
END;
Table function concepts, function
15. • Queried with the TABLE function
SELECT *
FROM TABLE(now);
Table function concepts, function
16. CREATE OR REPLACE FUNCTION now RETURN now_tt AS
l_returnvalue now_tt := now_tt();
l_now VARCHAR2(8);
BEGIN
FOR counter IN 1 .. 4 LOOP
l_now := to_char(SYSDATE, 'HH24:MI:SS');
dbms_lock.sleep(1);
l_returnvalue.extend;
l_returnvalue(l_returnvalue.last) := now_t(l_now);
END LOOP;
RETURN l_returnvalue;
END;
Table function concepts, simple function
17. SQL> SELECT *
2 FROM TABLE(now)
3 /
NOW
--------
16:54:21
16:54:22
16:54:23
16:54:24
4 rows selected.
Table function concepts, simple function
18. • Query results passed as a parameter
– Parameter type is REF CURSOR
– SYS_REFCURSOR can be used
for weakly typed option
CREATE OR REPLACE FUNCTION now_and_then
(cursor_in SYS_REFCURSOR )
RETURN now_and_then_tt AS
Table function concepts, nesting functions
19. • CURSOR keyword denotes value passed
within query
SELECT *
FROM TABLE(now_and_then( CURSOR ( SELECT *
FROM TABLE(now))))
/
Table function concepts, nesting functions
20. CREATE OR REPLACE FUNCTION now_and_then
(cursor_in SYS_REFCURSOR)
RETURN now_and_then_tt AS
l_returnvalue now_and_then_tt := now_and_then_tt();
l_now VARCHAR2(8);
l_then VARCHAR2(8);
BEGIN
LOOP
FETCH cursor_in
INTO l_now;
EXIT WHEN cursor_in%NOTFOUND;
l_then := to_char(SYSDATE, 'HH24:MI:SS');
l_returnvalue.extend;
l_returnvalue(l_returnvalue.last) :=
now_and_then_t(l_now, l_then);
END LOOP;
RETURN l_returnvalue;
END;
Table function concepts, nested function
21. SQL> SELECT *
2 FROM TABLE ( now_and_then( CURSOR( SELECT *
3 FROM TABLE ( now ))))
4 /
NOW AND_THEN
-------- --------
16:58:51 16:58:55
16:58:52 16:58:55
16:58:53 16:58:55
16:58:54 16:58:55
4 rows selected.
Table function concepts, nested function
22. • Function can be pipelined
• Produce results as they are created
– Returning results one record at a time
Pipelined table functions
23. • Adding PIPELINED keyword to function
specification
• Actual return datatype is collection
• PIPE ROW function returns single record to
calling process and then continues processing
• Return is still required
CREATE OR REPLACE FUNCTION now
RETURN now_t
PIPELINED AS
l_returnvalue single_time_t;
BEGIN
loop
...
PIPE ROW(l_returnvalue);
...
end loop;
RETURN;
END;
Pipelining : syntax
24. CREATE OR REPLACE FUNCTION now
RETURN now_tt
PIPELINED AS
l_returnvalue now_t;
l_now VARCHAR2(8);
BEGIN
FOR counter IN 1 .. 4 LOOP
dbms_lock.sleep(2);
l_now := to_char(SYSDATE, 'HH24:MI:SS');
l_returnvalue := now_t(l_now);
PIPE ROW (l_returnvalue);
END LOOP;
RETURN;
END;
Pipelined function
25. SQL> SELECT *
2 FROM TABLE(now_and_then(CURSOR(SELECT *
3 FROM TABLE(now))))
4 /
NOW AND_THEN
-------- --------
19:54:15 19:54:15
19:54:17 19:54:17
19:54:19 19:54:19
3 rows selected.
Pipelined function
Without pipelining
it would be:
NOW AND_THEN
-------- --------
19:54:15 19:54:19
19:54:17 19:54:19
19:54:19 19:54:19
now_and_then.sql
26. • Functions can be parallelized
• If the source data can be processed in
parallel, the functions can be processed
in parallel
Parallel table functions
27. Typical data processing
Stage 1
OLTP
F1 F2 Data
Warehouse
Stage 2 F3
Data goes through several transformations,
in table functions,
and then gets loaded into a database
28. Parallel & pipelined data processing
OLTP
F1 Data
Warehouse
F1
F1
F2
F2
F2
F3
F3
F3
Data goes through several transformations,
in table functions, in parallel (multiple
processes)
and then gets loaded into a database
29. • Prior to Oracle9i, calling a function
inside a SQL statement caused
serialization.
– The parallel query mechanism could not
be used.
• Now you can enable parallel execution
of a table function.
– This greatly increases the usability of
PL/SQL-enriched SQL in data warehouse
applications.
Parallel execution and table functions
parallel.sql
30. • The table function's parameter list must consist
only of a single strongly-typed REF CURSOR.
• Include the PARALLEL_ENABLE hint in the
program header.
– Choose a partition option that specifies how the
function's execution should be partitioned.
– "ANY" means that the results are independent of the
order in which the function receives the input rows
(through the REF CURSOR).
Enabling parallel execution
{[ORDER | CLUSTERORDER | CLUSTERORDER | CLUSTERORDER | CLUSTER] BY column_list}
PARALLEL_ENABLEPARALLEL_ENABLEPARALLEL_ENABLEPARALLEL_ENABLE ({PARTITIONPARTITIONPARTITIONPARTITION p BYBYBYBY
[ANY | (HASH | RANGE) column_list]} )
31. PARALLEL_ENABLE (
Partition p_input_rows BY ANY )
CREATE OR REPLACE FUNCTION Aggregate_Xform (
p_input_rows in My_Types.cur_t) RETURN
My_Types.dept_sals_tab
PIPELINED
CLUSTER P_INPUT_ROWS BY (dept)
PARALLEL_ENABLE
( PARTITION p_input_rows
BY HASH (dept) )
ORDER p_input_rows BY (c1, c2)
PARALLEL_ENABLE
( PARTITION p_input_rows
BY RANGE (c1) )
with
with
with
Simplest form, results don't
vary from order in which
function gets input rows.
All rows for a given department
must go to the same slave,
and rows are delivered
consecutively.
Rows are delivered to a
particular slave as directed by
partition... and will be locally
sorted by that slave.
Examples of parallelized functions
34. - Using PL/SQL in SQL
- Create script based on the table data
Using the table function
35. • Using SQL
Create script based on the table data
DEPT
(table)
SQL
statement
insert into DEPT( LOC, DNAME, DEPTNO)
values ( 'NEW YORK', 'ACCOUNTING', 10);
insert into DEPT( LOC, DNAME, DEPTNO)
values ( 'DALLAS', 'RESEARCH', 20);
insert into DEPT( LOC, DNAME, DEPTNO)
values ( 'CHICAGO', 'SALES', 30);
insert into DEPT( LOC, DNAME, DEPTNO)
values ( 'BOSTON', 'OPERATIONS', 40);
36. SQL
statement
SELECT 'insert into DEPT( LOC, DNAME, DEPTNO) values
( '''||
LOC||''', '''||
DNAME||''', '||
DEPTNO||');' line
FROM DEPT;
• Using SQL
Create script based on the table data
38. • Using table functions
• Hide complexity behind PL/SQL
interface
• Don’t worry about datatypes
Create script based on the table data
39. Overview of Table Functions
Table Function Concepts
Return collection, query like table
Pipelined
Parallel
UsingTable Functions
Use PL/SQL in SQL
Hide complexity
Table functions
40. tahiti.oracle.com
For all documentation online
Oracle PL/SQL Programming (the
book)
Especially chapter 17 (by Steven
Feuerstein) and chapter 21 (by
Steven Feuerstein with help from
Adrian Billington)
References