8/8/2019 SQL Games We Can Play in PLSQL
1/31
Copyright 2000-2006 Steven Feuerstein - Page 1
SQL Games We Can Playinside PL/SQL
Steven [email protected]
Quest Software
8/8/2019 SQL Games We Can Play in PLSQL
2/31
Copyright 2000-2006 Steven Feuerstein - Page 2
Software used in presentation
You can d ow nlo ad all my trainingmaterials and dem o nstrati o n scriptsfr o m:
All scripts I r u n may be o btained fr o m:
name of file
h ttp:// o racleplsqlpr o gramming.c o m/res ou rces. h tml
h ttp:// o racleplsqlpr o gramming.c o m/d ow nlo ads/dem o .zip
8/8/2019 SQL Games We Can Play in PLSQL
3/31
Copyright 2000-2006 Steven Feuerstein - Page 3
Let the Games Begin!
T u rb o ch arged SQL w ith BULKCOLLEC T and FORALLDeceptively simple SQL w ith tablef u ncti o nsCo de ab o ve t h e fray w ith rec o rd-levelDML.
8/8/2019 SQL Games We Can Play in PLSQL
4/31
Copyright 2000-2006 Steven Feuerstein - Page 4
Turbo-charged SQ L withBULK CO LLECT and FORA LL
Impr o ve t h e perf o rmance o f m u lti-r ow SQLo perati o ns by an o rder o f magnit u de o r m o rew ith bu lk/array pr o cessing in PL/SQL!CREATE OR REPLACE PROCEDURE upd_for_dept (
dept_in IN employee.department_id%TYPE,newsal_in IN employee.salary%TYPE)
ISCURSOR emp_cur IS
SELECT employee_id,salary,hire_dateFROM employee WHERE department_id = dept_in;
BEGINFOR rec IN emp_cur LOOP
UPDATE employee SET salary = newsal_in WHERE employee_id = rec.employee_id;
END LOOP;END upd_for_dept;
C onventional binds (and lots of them!)
8/8/2019 SQL Games We Can Play in PLSQL
5/31
Copyright 2000-2006 Steven Feuerstein - Page 5
Oracle server
PL/SQL R u ntime Engine SQL Engine
PL/SQL blockPr ocedu r al
statementexecuto r
SQL statementexecuto r
FOR rec IN emp_cur LOOPUPDATE employee
SET salary = ... WHERE employee_id =
rec.employee_id;END LOOP;
Performance penalty Performance penalty for many context for many context switches switches
Conventional Bind
8/8/2019 SQL Games We Can Play in PLSQL
6/31
Copyright 2000-2006 Steven Feuerstein - Page 6
Enter the Bulk Bind
Oracle server
PL/SQL R u ntime Engine SQL Engine
PL/SQL blockPr ocedu r al
statementexecuto r
SQL statementexecuto r
FORALL indx INdeptlist.FIRST..deptlist.LAST
UPDATE employeeSET salary = ...
WHERE employee_id =deptlist(indx);
Mu ch less overhead for Mu ch less overhead for context switching context switching
8/8/2019 SQL Games We Can Play in PLSQL
7/31
Copyright 2000-2006 Steven Feuerstein - Page 7
Use the FORA LL Bulk Bind Statement
Instead o f exec u ting repetitive, individ u al DMLstatements, y ou can w rite y ou r c o de like t h is:
Th ings t o be a w are o f: Y ou MUST know how to use collections to use this feature! O
nly a single DML statement is allowed per F ORA
LL. SQL%BULK_ ROW C O UNT returns the number of rows affected by each row in the binding array.
P rior to O racle10g, the binding array must be sequentially filled. Use S AVE EX C EP TI O NS to continue past errors.
PROCEDURE remove_emps_by_dept (deptlist dlist_t)ISBEGIN
FORALL aDept IN deptlist.FIRST..deptlist.LASTDELETE FROM emp WHERE deptno = deptlist(aDept);
END;
bulktiming.sqlbulk_ r owcount.sql
bulkexc.sql
8/8/2019 SQL Games We Can Play in PLSQL
8/31
Copyright 2000-2006 Steven Feuerstein - Page 8
Use BU LK CO LLECT IN TO for Queries
DECLARETYPE employees_aat IS TABLE OF employees%ROWTYPE
INDEX BY BINARY_INTEGER;
l_employees employees_aat;
BEGINSELECT *BULK COLLECT INTO l_employees
FROM employees;
FOR indx IN 1 .. l_employees.COUNTLOOP
process_employee (l_employees(indx));END LOOP;
END;
bulkcoll.sql
Decla r e acollection of
r eco r ds to holdthe que r ied data.
Use BU LK CO LLE CT to
r et r ieve all r ows.
Ite r ate th r ough thecollection
contents with aloop.
8/8/2019 SQL Games We Can Play in PLSQL
9/31
Copyright 2000-2006 Steven Feuerstein - Page 9
Limit the number of rows returned byBULK CO LLECT
CREATE OR REPLACE PROCEDURE bulk_with_limit(deptno_in IN dept.deptno%TYPE)
ISCURSOR emps_in_dept_cur IS
SELECT *FROM emp
WHERE deptno = deptno_in;
TYPE emp_tt IS TABLE OF emp%ROWTYPE;emps emp_tt;
BEGINOPEN three_cols_cur;LOOP
FETCH emps_in_dept_curBULK COLLECT INTO emps
LIMIT 100;
EXIT WHEN emps.COUNT = 0;
process_emps (emps);END LOOP;
END bulk_with_limit;
Use the LIMIT clause with theINTO to manage the amount
of memo ry used with theBU LK CO LLE CT ope r ation.
WARNING!
BU LK CO LLE CT will not r aiseNO_DATA_FOUND if no r ows
a r e found.
Best to check contents of collection to confi r m thatsomething was r et r ieved.
bulklimit.sql
8/8/2019 SQL Games We Can Play in PLSQL
10/31
Copyright 2000-2006 Steven Feuerstein - Page 10
Tips and Fine Points
Use b u lk binds in t h ese circ u mstances: R ecurring SQL statement in P L/SQL loop.O racle
recommended threshold: five rows!
Bu
lk bind r u
les: C an be used with any kind of collection; C ollectionsubscripts cannot be expressions; The collections must be densely filled (pre-10g); If error occurs, prior successful DML statements are N O T RO LLE D B AC K.
Bu lk c o llects: C an be used with implicit and explicit cursors C ollection is always filled sequentially, starting at row 1
emplu.pkgcfl_to_bulk*.*
8/8/2019 SQL Games We Can Play in PLSQL
11/31
Copyright 2000-2006 Steven Feuerstein - Page 11
D ynamic FORA LL Ex ample
Th is example s h ow s t h e u se o f bu lk binding andco llecting, pl u s applicati o n o f th e RE T URNING cla u se.
CREATE TYPE NumList IS TABLE OF NUMBER;CREATE TYPE NameList IS TABLE OF VARCHAR2(15);
PROCEDURE update_emps (col_in IN VARCHAR2, empnos_in IN numList) ISenames NameList;
BEGINFORALL indx IN empnos_in.FIRST .. empnos_in.LAST
EXECUTE IMMEDIATE
'UPDATE emp SET ' || col_in || ' = ' || col_in|| ' * 1.1 WHERE empno = :1RETURNING ename INTO :2'USING empnos_in (indx )RETURNING BULK COLLECT INTO enames ;
...END;
Notice that empnos_inis indexed, but enames
is not.
Oracle9 i
8/8/2019 SQL Games We Can Play in PLSQL
12/31
Copyright 2000-2006 Steven Feuerstein - Page 12
D ynamic BU LK CO LLECT
Now you can even avoid the OP EN FOR and justgrab your rows in a single pass!
CREATE OR REPLACE PROCEDURE fetch_by_loc (loc_in IN VARCHAR2)IS
TYPE numlist_t IS TABLE OF NUMBER ;TYPE namelist_t IS TABLE OF VARCHAR2 ( 15 );emp_cv sys_refcursor ;empnos numlist_t ;enames namelist_t ;sals numlist_t ;
BEGINOPEN emp_cv FOR 'SELECT empno, ename FROM emp_' || loc_in ;FETCH emp_cv BULK COLLECT INTO empnos , enames ;
CLOSE emp_cv ;
EXECUTE IMMEDIATE 'SELECT sal FROM emp _' || loc_inBULK COLLECT INTO sals ;
END;With O r acle9iR2
you can also fetchinto collections of
r eco r ds.
Oracle9 i
8/8/2019 SQL Games We Can Play in PLSQL
13/31
Copyright 2000-2006 Steven Feuerstein - Page 13
Ex cellent Ex ception Handlingfor Bulk Operations in Oracle 9i D atabase R2
Allows you to continue past errors andobtain error information for each individualoperation (for dynamic and static SQ L).
CREATE OR REPLACE PROCEDURE load_books (books_in IN book_obj_list_t)IS
bulk_errors EXCEPTION;PRAGMA EXCEPTION_INIT ( bulk_errors, -24381 );
BEGINFORALL indx IN books_in.FIRST..books_in.LAST
SAVE EXCEPTIONSINSERT INTO book values (books_in(indx));
EXCEPTION
WHEN BULK_ERRORSTHENFOR indx in 1.. SQL%BULK_EXCEPTIONS.COUNTLOOP
log_error ( SQL%BULK_EXCEPTIONS(indx));END LOOP;
END;
Allows p r ocessing of allr ows, even afte r an
e rr o r occu r s.
New cu r so r att r ibute, a pseudo-
collection
bulkexc.sql
8/8/2019 SQL Games We Can Play in PLSQL
14/31
Copyright 2000-2006 Steven Feuerstein - Page 14
Should you ever use a cursor FOR loop?
Oracle10g will automatically optimize cursorFOR loops to perform at levels comparable toBULK CO LLECT! So what the heck, keep or write your cursor FOR
loops -- at least if they contain no DML .Also stick with a cursor FOR loop .... If you want to do comple x DM L processing on each
row as it is queried and possibly halt furtherfetching, and you can't use SAV E EXCEPTIONS .
Otherwise, moving to BU LK CO LLECT andFORA LL is a smart move!
10 g_optimize_cfl.sqlcfl_vs_bulkcollect.sql
cfl_to_bulk.sql
8/8/2019 SQL Games We Can Play in PLSQL
15/31
Copyright 2000-2006 Steven Feuerstein - Page 15
M ore fle x ibility with FORA LL
In Oracle10g, t h e FORALL driving array n o lo nger needs t o be pr o cessed seq u entially.Use t h e INDICES OF cla u se t o u se o nly t h er ow nu mbers defined in another array.Use t h e VALUES OF cla u se t o u se o nly t h ev alues defined in an o th er array.
Or acle 10 g
8/8/2019 SQL Games We Can Play in PLSQL
16/31
Copyright 2000-2006 Steven Feuerstein - Page 16
Using IN D IC ES OF
It o nlypr o cessesth e r ow sw ith r ow nu mbersmatc h ingth e d efine d rows o f th e
drivingarray.
Or acle 10 g
DECLARETYPE employee_aat IS TABLE OF employee.employee_id%TYPE
INDEX BY PLS_INTEGER;l_employees employee_aat;TYPE boolean_aat IS TABLE OF BOOLEAN
INDEX BY PLS_INTEGER;l_employee_indices boolean_aat;
BEGINl_employees (1) := 7839;l_employees (100) := 7654;l_employees (500) := 7950;--l_employee_indices (1) := TRUE;l_employee_indices (500) := TRUE;--FORALL l_index IN INDICES OF l_employee_indices
UPDATE employeeSET salary = 10000
WHERE employee_id = l_employees (l_index);END;
10g_indices_ o f.sql
8/8/2019 SQL Games We Can Play in PLSQL
17/31
Copyright 2000-2006 Steven Feuerstein - Page 17
Using VA LUES OF
It o nlypr o cessesth e r ow s
w ith r ow nu mbersmatc h ingth e c ontent o f a r ow in
th e drivingarray.
Or acle 10 g
DECLARETYPE employee_aat IS TABLE OF employee.employee_id%TYPE
INDEX BY PLS_INTEGER;l_employees employee_aat;TYPE indices_aat IS TABLE OF PLS_INTEGER
INDEX BY PLS_INTEGER;l_employee_indices indices_aat;
BEGINl_employees (-77) := 7820;l_employees (13067) := 7799;l_employees (99999999) := 7369;--l_employee_indices (100) := -77;l_employee_indices (200) := 99999999;--
FORALL l_index IN VALUES OF l_employee_indicesUPDATE employeeSET salary = 10000
WHERE employee_id = l_employees (l_index);END;
10g_val u es_ o f.sql
8/8/2019 SQL Games We Can Play in PLSQL
18/31
Copyright 2000-2006 Steven Feuerstein - Page 18
Table Functions
A table f u ncti o n is a f u ncti o n t h at y ou can call in t h eFROM cla u se o f a q u ery, and h ave it be treated as if it w ere a relational table.T
able f u
nctio
ns allow
you
to
perf o
rm arbitrarilyco mplex transf o rmati o ns o f data and t h en make t h atdata available t h r ou gh a q u ery. Not everything can be done in SQL.
Co mbined w ith REF CURSORs, y ou can n ow m o reeasily transfer data fr o m w ith in PL/SQL t o h o stenvir o nments. J ava, for example, works very smoothly with cursor variables
8/8/2019 SQL Games We Can Play in PLSQL
19/31
Copyright 2000-2006 Steven Feuerstein - Page 19
Applications for pipelined functions
Exec u tio n f u ncti o ns in parallel. In O racle9i Database R elease 2 and above, you can use the
PARA LLE L_ E N ABLE clause to allow your pipelined
function to participate fully in a parallelized query. C ritical in data warehouse applications.
Impr o ve speed o f delivery o f data t o w ebpages. Use a pipelined function to "serve up" data to the webpage
and allow users to being viewing and browsing, evenbefore the function has finished retrieving all of the data.
8/8/2019 SQL Games We Can Play in PLSQL
20/31
Copyright 2000-2006 Steven Feuerstein - Page 20
Building a table function
A table f u ncti o n m u st ret u rn a nested table o r varray based o n a sc h ema-defined type, o r typedefined in a PL/SQL package.Th e f u ncti o n h eader and t h e w ay it is calledm u st be SQL-c o mpatible: all parameters u seSQL types; n o named n o tati o n. In some cases (streaming and pipelines functions), the IN
parameter must be a cursor variable -- a query result set.
8/8/2019 SQL Games We Can Play in PLSQL
21/31
Copyright 2000-2006 Steven Feuerstein - Page 21
Simple table function e xample
Ret u rn a list o f names as a nested table, andth en call t h at f u ncti o n in t h e FROM cla u se.
CREATE OR REPLACE FUNCTION lotsa_names (
base_name_in IN VARCHAR2, count_in IN INTEGER )RETURN names_nt
ISretval names_nt := names_nt ();
BEGINretval.EXTEND (count_in);
FOR indx IN 1 .. count_inLOOP
retval (indx) := base_name_in || ' ' || indx;
END LOOP;
RETURN retval;END lotsa_names; tabf
u nc_scalar.sql
SELECT column_valueFROM TABLE (
lotsa_names ('Steven', 100)) names;
COLUMN_VALUE
------------Steven 1...Steven 100
8/8/2019 SQL Games We Can Play in PLSQL
22/31
Copyright 2000-2006 Steven Feuerstein - Page 22
Streaming data with table functions
You can u se table f u ncti o ns t o "stream" data t h r ou gh several stages w ith in a single SQL statement.
CREATE TYPE tickertype AS OBJECT (ticker VARCHAR2 (20)
, pricedate DATE, pricetype VARCHAR2 (1), price NUMBER
);
CREATE TYPE tickertypeset AS TABLE OF tickertype;/
CREATE TABLE tickertable (ticker VARCHAR2(20),
pricedate DATE, pricetype VARCHAR2(1), price NUMBER)
/
tabf u nc_streaming.sql
8/8/2019 SQL Games We Can Play in PLSQL
23/31
Copyright 2000-2006 Steven Feuerstein - Page 23
Streaming data with table functions - 2
In th is example, transf o rm eac h r ow o f th est o cktable int o two r ow s in t h e tickertable.
CREATE OR REPLACE PACKAGE refcur_pkgIS
TYPE refcur_t IS REF CURSOR RETURN stocktable%ROWTYPE;
END refcur_pkg;/
CREATE OR REPLACE FUNCTION stockpivot (dataset refcur_pkg.refcur_t)RETURN tickertypeset ...
BEGININSERT INTO tickertable
SELECT *FROM TABLE (stockpivot (CURSOR (SELECT *
FROM stocktable)));END;/
tabf u nc_streaming.sql
8/8/2019 SQL Games We Can Play in PLSQL
24/31
Copyright 2000-2006 Steven Feuerstein - Page 24
Use pipelined functions to enhanceperformance .
Pipelined f u ncti o ns all ow you to ret u rn dataiteratively, async h r o nou s t o terminati o n o f th ef u ncti o n. As data is produced within the function, it is passed back
to the calling process/query.
Pipelined f u
nctio
ns can be defined to
su
ppo
rtparallel exec u tio n. Iterative data processing allows multiple processes to
work on that data simultaneously.
CREATE FUNCTION StockPivot (p refcur_ p kg.refcur_t ) RETURN TickerTy p eSet PIPELINED
8/8/2019 SQL Games We Can Play in PLSQL
25/31
Copyright 2000-2006 Steven Feuerstein - Page 25
Piping rows out from a pipelined function
CREATE FUNCTION stockpivot (p refcur_pkg.refcur_t)RETURN tickertypesetPIPELINED
ISout_rec tickertype :=
tickertype (NULL, NULL, NULL);in_rec p%ROWTYPE;
BEGINLOOPFETCH p INTO in_rec;EXIT WHEN p%NOTFOUND;out_rec.ticker := in_rec.ticker;out_rec.pricetype := 'O';out_rec.price := in_rec.openprice;
PIPE ROW (out_rec);END LOOP;CLOSE p;
RETURN;END;
tabf u nc_set u p.sqltabf u nc_pipelined.sql
Add P IPEL INEDke ywo r d to heade r
P ipe a r ow of databack to calling block
o r que ry
RETURN...nothing atall!
8/8/2019 SQL Games We Can Play in PLSQL
26/31
Copyright 2000-2006 Steven Feuerstein - Page 26
Enabling Parallel Ex ecution
Th e table f u ncti o n's parameter list m u st c o nsist o nlyo f a single strongly-type d REF CURSOR.Inclu de t h e PARALLEL_ENABLE h int in t h e pr o gramh eader. C hoose a partition option that specifies how the function's
execution should be partitioned. " AN Y " means that the results are independent of the order in which
the function receives the input rows (through the RE F C U R S OR ).
{[ ORDER | CLUSTER ] BY column_list } PARALLEL_ENABLE ( { PARTITION p BY
[ ANY | (H ASH | RANGE ) column_list ]} )
8/8/2019 SQL Games We Can Play in PLSQL
27/31
Copyright 2000-2006 Steven Feuerstein - Page 27
Table functions - Summary
T able f u ncti o ns o ffer significant ne w flexibilityf o r PL/SQL devel o pers.Co nsider u sing t h em w h en y ou ... Need to pass back complex result sets of data through the
SQL layer (a query); W ant to call a user defined function inside a query and
execute it as part of a parallel query.
8/8/2019 SQL Games We Can Play in PLSQL
28/31
Copyright 2000-2006 Steven Feuerstein - Page 28
Record-based DM L
PL/SQL rec o rds (similar in str u ctu re t o a r ow in a table) o ffer p ow erf u l w ays t o manip u latedata Pri o r to Oracle9i R2, h ow ever, rec o rds c ou ld n o t
be u sed in DML statementsTh at restricti o n h as n ow been lifted You can INSER T specifying a rec o rd rat h er t h an
individ u al fields o f th e rec o rd You can UPDA T E an entire r ow w ith a rec o rd
Oracle9 i
8/8/2019 SQL Games We Can Play in PLSQL
29/31
Copyright 2000-2006 Steven Feuerstein - Page 29
Th is example s h ow s a rec o rd-based insertinside t h e h igh -speed FORALL statement
DECLARETYPE book_list_t IS TABLE OF books%ROWTYPE;
my_books book_list_t := book_list_t();BEGIN
my_books.EXTEND (2);
my_books(1).isbn := '1-56592-335-9'; my_books(1).title := 'ORACLE PL/SQL PROGRAMMING';
my_books(2).isbn := '0-596-00121-5'; my_books(2).title := 'ORACLE PL/SQL BEST PRACTICES';
FORALL indx IN my_books.FIRST .. my_books.LASTINSERT INTO books VALUES my_books(indx);
END;
Record-based Inserts
8/8/2019 SQL Games We Can Play in PLSQL
30/31
Copyright 2000-2006 Steven Feuerstein - Page 30
You can o nly u pdate t h e entire ROW, and n o t as u bset via, say, a pr o grammer-defined rec o rd type
DECLARE my_book books%ROWTYPE;
BEGIN my_book.isbn := '1-56592-335-9'; my_book.title := 'ORACLE PL/SQL PROGRAMMING'; my_book.summary := 'General user guide and reference'; my_book.author := 'FEUERSTEIN, STEVEN AND BILL PRIBYL'; my_book.page_count := 950; -- new page count for 3rd edition
UPDATE booksSET ROW = my_book
WHERE isbn = my_book.isbn;END;
Record-based Updates
8/8/2019 SQL Games We Can Play in PLSQL
31/31
Copyright 2000-2006 Steven Feuerstein - Page 31
Stop taking SQ L for granted!
Every SQL statement is a h ard-c o ding o f you r data str u ctu res in y ou r c o de, s o ... C ontrol your SQL.
Avoid writing SQL. And fully leverage P L/SQL when you do write SQL.T ake advantage o f key PL/SQL f u ncti o nality. F ORA LL BULK C O LLE C T Table functions R ecord-based DML