PL/SQL is an acronym for Programming Language for SQL or just Programming Language SQL. PL/SQL is effectively a modular programming language and SQL scripting language. The only problems with PL/SQL are twofold. The first issue are it roots which are as an extension or wrapper for SQL, making it originally somewhat primitive, as relational database stored procedure languages usually are. The second issue with PL/SQL is that it is interpretive. Interpretive implies not compiled into binary and thus performance is slow. The result is that current versions of PL/SQL can be syntactically complex to the point of being capable of intensive application level complex processing. The obvious result is extremely poorly performing applications.
So what makes PL/SQL a programming language? Programming languages have certain constructs. Generally these constructs encompass some type of structural integrity, strong typing using variables and datatypes plus a set of programming controls. PL/SQL has the following constructs.
Two other factors must be considered for PL/SQL. The first is obviously database access since PL/SQL is a database access programming language and the second is Oracle provided PL/SQL packaged functionality. Let's start by going through each of the above mentioned aspects of PL/SQL in turn.
The following image shows how variables are declared and referred inside a block of PL/SQL code. Note how the VAL variable is set using the instantiation operator (:=).

Variable scope is the definable or accessible area or level of a variable. Examine the following graphic and note the following points.
VAR1 and VAR2 as defined for the outer block will retain their values within the inner blocks as long as they are not redeclared in the inner blocks.
VAR2 is redeclared in Inner Block 1 and VAR1 is redeclared in Inner Block 2. In other words during the processing of Inner Block 1, VAR2 is replaced by a new overriding declaration of VAR2 in Inner Block 1. When Inner Block 1 completes execution the value of VAR2 from the outer block is recovered to the declaration of VAR2 in the outer block. The same applies to VAR1 in between Inner Block 2 and the outer block.

A multitude of datatypes can be used in PL/SQL including all datatypes which can be used as table column definitions plus a few others which cannot. Also included in this section is a quick summary of object datatypes with reference to two other older web pages, covering objects and object datatypes available in Oracle plus large object datatypes use in Oracle.
Some datatypes are specific to PL/SQL and cannot be used in table creation.
set serveroutput on;
CREATE OR REPLACE PROCEDURE true_or_false AS
i BOOLEAN DEFAULT TRUE;
BEGIN
IF i THEN
dbms_output.put_line('TRUE');
END IF;
END;
/
EXEC true_or_false;
DECLARE I INTEGER; Subj subject.name%TYPE; Subjs subject%ROWTYPE;
A TYPE allows creation of a user defined type. See RECORD below.
TYPE type IS … user defined type …
A RECORD permits a structural definition of a sequence of columns or types.
TYPE type IS RECORD ( column%TYPE [, column%TYPE … ] );
The following example declares a RECORD (rAuthor) as a copy of the Author table structure, creates a new TYPE with a structure not in any table (tPublication), and finally creates a RECORD against the newly created TYPE (rPublication).
DECLARE rAuthor author%ROWTYPE; TYPE tPublication IS RECORD( record# INTEGER, book publication.title%TYPE); rPublication tPublication;
Object and object datatype definitions encompass collections, reference pointers, classes and methods plus binary object storage.
A Nested Table is a dynamic array which is a pointer.
DECLARE TYPE tTable IS TABLE OF VARCHAR2(32); vPointer tTable;
Let's look for example at the Subject table in the Books schema.
SUBJECT_ID PARENT_ID NAME
---------- ---------- --------------------------------
1 Non-Fiction
2 1 Self Help
3 1 Esoteric
4 1 Metaphysics
5 1 Computers
6 Fiction
7 6 Science Fiction
8 6 Fantasy
9 6 Drama
10 6 Whodunnit
11 6 Suspense
12 6 Literature
13 12 Poetry
14 12 Victorian
15 12 Shakespearian
16 12 Modern American
17 12 19th Century American
Now I am adding a nested table collection to the Subject table to cater for the hierarchy.
CREATE OR REPLACE TYPE SubjectCollection AS TABLE OF VARCHAR2(32);
/
ALTER TABLE subject ADD(subjects SubjectCollection) NESTED TABLE subjects STORE AS subjectsTable;
UPDATE subject SET subjects =
SubjectCollection('Self Help','Estoric','Metaphysics','Computers')
WHERE name = 'Non-Fiction';
UPDATE subject SET subjects =
SubjectCollection('Science Fiction','Fantasy','Drama','Whodunnit','Suspense','Literature')
WHERE name = 'Fiction';
SELECT * FROM TABLE(SELECT subjects FROM subject WHERE name = 'Fiction');
COLUMN_VALUE
--------------------------------
Science Fiction
Fantasy
Drama
Whodunnit
Suspense
Literature
A VARRAY is a fixed length array which is a directly accessible chunk of memory.
DECLARE TYPE tVARRAY IS VARRAY(100) OF INTEGER; vArray tVARRAY;
Now let's change the SubjectCollection object and add a second layer.
ALTER TABLE subject DROP COLUMN subjects; CREATE OR REPLACE TYPE tSubSubjectCollection AS VARRAY(100) OF VARCHAR2(32); / CREATE OR REPLACE TYPE tSubjectCollection AS OBJECT(name VARCHAR2(32),subjects tSubSubjectCollection); / CREATE OR REPLACE TYPE tSubjectHierarchy AS VARRAY(100) OF tSubjectCollection; / ALTER TABLE subject ADD(subjects tSubjectHierarchy);
Now let's add the data again.
UPDATE subject SET subjects = NULL;
UPDATE subject SET subjects =
tSubjectHierarchy
(
tSubjectCollection('Self Help',NULL),
tSubjectCollection('Estoric',NULL),
tSubjectCollection('Metaphysics',NULL),
tSubjectCollection('Computers',NULL)
)
WHERE name = 'Non-Fiction';
UPDATE subject SET subjects =
tSubjectHierarchy
(
tSubjectCollection('Science Fiction',NULL),
tSubjectCollection('Fantasy',NULL),
tSubjectCollection('Drama',NULL),
tSubjectCollection('Whodunnit',NULL),
tSubjectCollection('Suspense',NULL),
tSubjectCollection('Literature',
tSubSubjectCollection('Poetry','Victorian','Shakespearian','Modern American','19th Century American')
)
)
WHERE name = 'Fiction';
SELECT * FROM TABLE(SELECT subjects FROM subject WHERE name = 'Fiction');
NAME SUBJECTS
-------------------------------- -----------------------------------------------
Science Fiction
Fantasy
Drama
Whodunnit
Suspense
Literature TSUBSUBJECTCOLLECTION('Poetry', 'Victorian', 'S
An Associative Array is an indexed dynamic array and the most efficient of all three collection datatypes.
DECLARE TYPE tITable IS TABLE OF NUMBER INDEX BY BINARY_INTEGER; vIndexedPointer tITable;
Reference datatypes can be used to reference or dereference collection elements using type casting, much like object type casting of classes in order to cast a class as an abstract or a specialization of another class within a hierarchical structure of classes, applying the type casted class definition, inluding its methods and types.
x := Class(Class(Object))
Classes with associated or encapsulated methods and processing can be constructed in PL/SQL using constructor, member, map and order methods.
PL/SQL programming controls can be broken into three section of selection, loops and sequences.
IF conditional expression 1 THEN … [ ELSIF conditional expression 2 THEN ] … [ ELSE … ] END IF;
Case statements can occur in one of two forms.
CASE WHEN search condition 1 THEN … [ WHEN search condition 2 THEN … ] [ ELSE … ] END CASE;
CASE selector WHEN expression 1 THEN … [ WHEN expression 2 THEN ] … [ ELSE … ] END CASE;
FOR counter IN [ REVERSE ] lower..higher LOOP … EXIT WHEN expression; END LOOP;
WHILE condition LOOP … [ EXIT WHEN expression; ] END LOOP;
This loop has two forms where the first shown is the equivalent of a WHILE loop since the EXIT WHEN option is at the start of the LOOP.
LOOP [ EXIT WHEN expression; ] … END LOOP;
The second form is the equivalent of an UNTIL loop since the EXIT WHEN option is at the end of the loop.
LOOP … [ EXIT WHEN expression; ] END LOOP;
As seen in the diagram GOTO allows label definitions and specified jumps to those labels.

BEGIN … EXCEPTION WHEN OTHERS THEN NULL; END; / BEGIN NULL; END; /
As can be seen in the following diagram blocks are delineated by the keywords BEGIN and END. Additionally blocks can be nested within blocks.

The next image shows multiple exception traps, each applicable to the block they are contained within. An expception occuring within a block without an exception trap will cause the exception to be passed outwards to the calling block or procedure.

Blocks can be named or anonymous. A named procedure can be stored in the database and re-executed, an anonymouns procedure cannot.

The following procedure is not named and thus anonymous. When entered into a tool such as SQL*Plus it will be interpreted and executed, once.
SET SERVEROUTPUT ON; DECLARE i INTEGER; BEGIN FOR i IN 1..10 LOOP DBMS_OUTPUT.PUT_LINE(TO_CHAR(i)||' of to 10'); END LOOP; EXCEPTION WHEN OTHERS THEN ROLLBACK; RAISE; END; / SET SERVEROUTPUT OFF;
CREATE [ OR REPLACE ] PROCEDURE [schema.]procedure
( parameter { IN | OUT | IN OUT } datatype [, ... ] )
{ IS | AS }
BEGIN
… PL/SQL statements …
[ EXCEPTION WHEN condition THEN ]
… PL/SQL statements …
END;
/
Parameter settings are as follows.
ALTER PROCEDURE [schema.]procedure COMPILE [ DEBUG ] [ REUSE SETTINGS ]; DROP PROCEDURE [schema.]procedure;
Let's say we wanted to increase the LIST_PRICE column values for a particular publisher. Let's examine editions printed by a specific publisher first.
COL title FORMAT A16 HEADING "Publication"; SELECT pl.title, e.list_price FROM publication pl JOIN edition e USING (publication_id) JOIN publisher pu USING (publisher_id) WHERE pu.name = 'Del Rey Books'; Publication LIST_PRICE ---------------- ---------- Foundation 7.99 Foundation 4.99 Footfall 7.99
This procedure will change prices of all editions for a specified publisher by a ratio.
CREATE OR REPLACE PROCEDURE PriceIncrease(pPublisher IN VARCHAR2, pIncrease IN FLOAT) AS
BEGIN
IF pPublisher IS NOT NULL AND pIncrease IS NOT NULL THEN
UPDATE edition SET list_price = ROUND(list_price * pIncrease, 2)
WHERE publisher_id = (SELECT publisher_id FROM publisher WHERE name = pPublisher);
END IF;
COMMIT;
EXCEPTION WHEN OTHERS THEN
ROLLBACK;
RAISE;
END;
/
ALTER PROCEDURE PriceIncrease COMPILE;
EXEC PriceIncrease('Del Rey Books',1.1);
Now let's check the new prices.
COL title FORMAT A16 HEADING "Publication"; SELECT pl.title, e.list_price FROM publication pl JOIN edition e USING (publication_id) JOIN publisher pu USING (publisher_id) WHERE pu.name = 'Del Rey Books'; Publication LIST_PRICE ---------------- ---------- Foundation 8.79 Foundation 5.49 Footfall 8.79
A function is defined in the same way as a procedure except that it returns a value.
CREATE [ OR REPLACE ] FUNCTION [schema.]procedure
( parameter { IN | OUT | IN OUT } datatype [, ... ] ) RETURN datatype
{ IS | AS }
BEGIN
… PL/SQL statements …
[ EXCEPTION WHEN condition THEN ]
… PL/SQL statements …
END;
/
ALTER FUNCTION [schema.]procedure COMPILE
[ DEBUG ] [ REUSE SETTINGS ];
DROP FUNCTION [schema.]procedure;
Here is an example function.
--
--F = (C * 9/5) + 32
--
CREATE OR REPLACE FUNCTION CToF(C IN NUMBER DEFAULT 0) RETURN NUMBER IS
BEGIN
RETURN ROUND(((C * 9/5) + 32), 0);
END;
/
ALTER FUNCTION CToF COMPILE;
/
set serveroutput on;
exec dbms_output.put_line('0C = '||TO_CHAR(CToF(0))||'F.');
exec dbms_output.put_line('10C = '||TO_CHAR(CToF(10))||'F.');
exec dbms_output.put_line('20C = '||TO_CHAR(CToF(20))||'F.');
exec dbms_output.put_line('30C = '||TO_CHAR(CToF(30))||'F.');
exec dbms_output.put_line('40C = '||TO_CHAR(CToF(40))||'F.');
set serveroutput off;
DROP FUNCTION CToF;
A trigger is used to detect database events where the trigger responds to that event by executing its code. Triggers can be used to maintain Referential Integrity of data or for logging and auditing. Since triggers are PL/SQL which is interpretive and not compiled, extensive use of triggers can cause serious performance problems. An additional issue with using triggers is convoluted and very large transactions. This is because triggers cannot contain transactional completion commands such as COMMIT or ROLLBACK. Triggers are not supposed to contain COMMIT or ROLLBACK commands due to their nature but still, use of triggers can cause serious performance problems as a result.
A trigger can be executed before or after an event or as a replacement for another event (instead of).
CREATE [ OR REPLACE ] TRIGGER [schema.]trigger
{ BEFORE | AFTER | INSTEAD OF }
{ INSERT [ OR UPDATE [ OF column [, column ... ] ] [ OR DELETE ] ] }
ON [schema.]table
[ REFERENCING
{ OLD [ AS ] old | NEW [ AS ] new | PARENT [ AS ] parent } ]
[ FOR EACH ROW ]
[ WHEN ( condition ) ];
Any combination of insert, update and delete events can be detected and reacted upon.
CREATE [ OR REPLACE ] TRIGGER [schema.]trigger
{ BEFORE | AFTER | INSTEAD OF }
{ INSERT [ OR UPDATE [ OF column [, column ... ] ] [ OR DELETE ] ] }
ON [schema.]table
[ REFERENCING
{ OLD [ AS ] old | NEW [ AS ] new | PARENT [ AS ] parent } ]
[ FOR EACH ROW ]
[ WHEN ( condition ) ];
Column values can be referenced as old.column and new.column. The REFERENCING clause can rename old. and new..
CREATE [ OR REPLACE ] TRIGGER [schema.]trigger
{ BEFORE | AFTER | INSTEAD OF }
{ INSERT [ OR UPDATE [ OF column [, column ... ] ] [ OR DELETE ] ] }
ON [schema.]table
[ REFERENCING
{ OLD [ AS ] old | NEW [ AS ] new | PARENT [ AS ] parent } ]
[ FOR EACH ROW ]
[ WHEN ( condition ) ];
A trigger specified as FOR EACH ROW will trigger the event for every row operated on in a SQL code statement. Otherwise the trigger is fired once for each statement, regardless of how many rows the statement operates on.
CREATE [ OR REPLACE ] TRIGGER [schema.]trigger
{ BEFORE | AFTER | INSTEAD OF }
{ INSERT [ OR UPDATE [ OF column [, column ... ] ] [ OR DELETE ] ] }
ON [schema.]table
[ REFERENCING
{ OLD [ AS ] old | NEW [ AS ] new | PARENT [ AS ] parent } ]
[ FOR EACH ROW ]
[ WHEN ( condition ) ];
The WHEN clause specifies a SQL conditional clause which even though the trigger may be fired by a DML event, it can be aborted. DML event triggers must be declared as FOR EACH ROW for the WHEN condition to be used.
CREATE [ OR REPLACE ] TRIGGER [schema.]trigger
{ BEFORE | AFTER | INSTEAD OF }
{ INSERT [ OR UPDATE [ OF column [, column ... ] ] [ OR DELETE ] ] }
ON [schema.]table
[ REFERENCING
{ OLD [ AS ] old | NEW [ AS ] new | PARENT [ AS ] parent } ]
[ FOR EACH ROW ]
[ WHEN ( condition ) ];
ALTER TRIGGER [schema.]trigger
{ ENABLE | DISABLE
| RENAME TO trigger
| COMPILE [ DEBUG ] [ RESUSE SETTINGS ] };
DROP TRIGGER [schema.]procedure;
Let's create a log file for events occuring on the Review table in the Books schema.
CREATE TABLE log (event VARCHAR2(128), event_date DATE DEFAULT SYSDATE);
CREATE OR REPLACE TRIGGER logI
AFTER INSERT ON review FOR EACH ROW
BEGIN
INSERT INTO log(event) VALUES('New review inserted for '||
(SELECT title FROM publication WHERE publication_id = :new.publication_id)||'.');
END;
/
CREATE OR REPLACE TRIGGER logU
AFTER UPDATE OF text ON review FOR EACH ROW
BEGIN
INSERT INTO log(event) VALUES('Review changed for '||
(SELECT title FROM publication WHERE publication_id = :old.publication_id)||'.');
END;
/
CREATE OR REPLACE TRIGGER logD
AFTER DELETE ON review FOR EACH ROW
BEGIN
INSERT INTO log(event) VALUES('Review deleted for '||
(SELECT title FROM publication WHERE publication_id = :old.publication_id)||'.');
END;
/
INSERT INTO review VALUES(
review_seq.NEXTVAL
,(SELECT publication_id FROM publication WHERE title = 'Second Foundation')
,SYSDATE
,'This is a review');
UPDATE review SET text = 'The review text has been changed'
WHERE publication_id = (SELECT publication_id FROM publication WHERE title = 'Second Foundation');
DELETE FROM review WHERE publication_id = (SELECT publication_id FROM publication WHERE title = 'Second Foundation');
Now let's look at our log table.
COLUMN event FORMAT A64; SELECT event_date, event FROM log; EVENT_DAT EVENT --------- ----------------------------------------------- 15-OCT-03 New review inserted for Second Foundation. 15-OCT-03 Review changed for Second Foundation. 15-OCT-03 Review changed for Second Foundation. 15-OCT-03 Review deleted for Second Foundation. 15-OCT-03 Review deleted for Second Foundation.
I could disable triggers and prevent a response.
ALTER TRIGGER trigger name DISABLE;
In the following syntax diagram for PL/SQL SELECT operations placeholder variables are required for each expression in the SELECT statement list.
SELECT { [alias.]column | expression | [alias.]* [ , … ] }
INTO variable [, variable …] FROM [schema.]table [alias]
[ WHERE [ [schema.]table.|alias.] { column | expression }
comparison { … } [ { AND | OR } [ NOT ] … ] ]
[ ORDER BY { { column | expression | position }
[ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, … ] } ]
[ GROUP BY { expression | rollup-cube | grouping-sets }
[, { expression | rollup-cube | grouping-sets } ... ]
[ HAVING condition ]
];
The INSERT, UPDATE and DELETE statements have a RETURNING clause in addition for DML in PL/SQL. The RETURNING clause can return single variables for a single row or arrays for multiple rows.
INSERT INTO [schema.]table [ ( column [, column …] ) ]
VALUES ( { expr | DEFAULT | (subquery) }
[, { expr | DEFAULT | (subquery) } ... ] )
[ RETURNING expr [, expr ] INTO variable [, variable ] ];
UPDATE [schema.]table
SET column = expr | (subquery) | DEFAULT
| ( column [, column … ] ) = (subquery)
[ WHERE ... ]
[ RETURNING expr [, expr] INTO variable [, variable ] ) ];
DELETE [ FROM ] [schema.]table
[ WHERE ... ]
[ RETURNING expr [, expr] INTO variable [, variable ] ) ];
Here is an anonymous procedure retrieving a single value.
SET SERVEROUTPUT ON;
DECLARE
i INTEGER;
BEGIN
SELECT COUNT(*) INTO i FROM subject;
DBMS_OUTPUT.PUT_LINE('There are '||TO_CHAR(i)||' Subjects in the database');
END;
/
This example retrieves more than one column. Note how variable names can be the same as column names.
DECLARE
subject_id subject.subject_id%TYPE;
name subject.name%TYPE;
BEGIN
SELECT subject_id, name INTO subject_id, name
FROM subject WHERE ROWNUM = 1;
DBMS_OUTPUT.PUT_LINE('The first subject stored is '||TRIM(TO_CHAR(subject_id))||': '||TRIM(name));
END;
/
Let's use the RETURNING INTO clause.
DECLARE author_id author.author_id%TYPE; publisher_id publisher.publisher_id%TYPE; publication_id publication.publication_id%TYPE; BEGIN INSERT INTO author VALUES(author_seq.nextval, 'Kurt Vonnegut') RETURNING author_id INTO author_id; INSERT INTO publisher VALUES(publisher_seq.nextval, 'Berkley Publishing Group') RETURNING publisher_id INTO publisher_id; INSERT INTO publication VALUES(publication_seq.nextval, (SELECT subject_id FROM subject WHERE name = 'Modern American'), author_id, 'Hocus Pocus') RETURNING publication_id INTO publication_id; INSERT INTO edition VALUES(0425130215, publisher_id, publication_id, '30-NOV-91', NULL, 7.99, 'Paperback', 98173, NULL); COMMIT; EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE(SQLERRM(SQLERRM)); ROLLBACK; END; /
Cursors are used by PL/SQL to allow for operations on more than a single row. There are two types of cursors, implicit and explicit. As the two different terms imply, the former is built automatically and the latter is constructed manually using PL/SQL commands. Cursors allow scrolling through a set of retrieved rows. In theory every SELECT statement creates a cursor because a cursor is the chunk of memory allocated to the results of a SELECT statement.
The internal cursor SQL. can be inspected to find values for the most recently executed cursor.
BEGIN
DELETE FROM author WHERE name = 'Douglas Adams';
IF SQL%NOTFOUND THEN
INSERT INTO author VALUES(author_seq.nextval, 'Douglas Adams');
DBMS_OUTPUT.PUT_LINE('New author added.');
ELSE
DBMS_OUTPUT.PUT_LINE('Author deleted.');
END IF;
COMMIT;
EXCEPTION WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLERRM(SQLCODE));
ROLLBACK;
END;
/
There is another way.
DECLARE
author_id author.author_id%TYPE;
BEGIN
BEGIN
SELECT author_id INTO author_id FROM author WHERE name = 'Douglas Adams';
DELETE FROM author WHERE name = 'Douglas Adams';
DBMS_OUTPUT.PUT_LINE('Author Deleted.');
EXCEPTION WHEN NO_DATA_FOUND THEN
INSERT INTO author VALUES(author_seq.nextval, 'Douglas Adams');
DBMS_OUTPUT.PUT_LINE('New author added.');
END;
COMMIT;
EXCEPTION WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLERRM(SQLCODE));
ROLLBACK;
END;
/
A cursor FOR loop is an creates an implicit cursor.
DECLARE CURSOR cAuthors IS SELECT * FROM author ORDER BY name; BEGIN FOR rAuthor IN cAuthors LOOP DBMS_OUTPUT.PUT_LINE(rAuthor.name); END LOOP; END; /
Explicit cursors have the following characteristics in relation to implicit cursors.
Here is an example of an explicit cursor.
DECLARE CURSOR cAuthors IS SELECT author_id, name FROM author ORDER BY name; rAuthor Author%ROWTYPE; BEGIN OPEN cAuthors; LOOP FETCH cAuthors INTO rAuthor; EXIT WHEN cAuthors%NOTFOUND; DBMS_OUTPUT.PUT_LINE(rAuthor.name); END LOOP; CLOSE cAuthors; EXCEPTION WHEN OTHERS THEN CLOSE cAuthors; RAISE; END; /
This example creates an explicit cursor generically, which can be altered at runitime.
DECLARE TYPE tAuthor IS REF CURSOR RETURN author%ROWTYPE; cAuthors tAuthor; rAuthor author%ROWTYPE; BEGIN OPEN cAuthors FOR SELECT author_id, name FROM author WHERE name LIKE '%a%'; LOOP FETCH cAuthors INTO rAuthor; EXIT WHEN cAuthors%NOTFOUND; DBMS_OUTPUT.PUT_LINE(rAuthor.name); END LOOP; CLOSE cAuthors; EXCEPTION WHEN OTHERS THEN CLOSE cAuthors; RAISE; END; /
This example is also generic but using a parameter.
CREATE OR REPLACE PROCEDURE getAuthors(pattern IN VARCHAR2 DEFAULT NULL) AS
TYPE tAuthor IS REF CURSOR RETURN author%ROWTYPE;
cAuthors tAuthor;
rAuthor Author%ROWTYPE;
BEGIN
OPEN cAuthors FOR SELECT author_id, name FROM author WHERE name LIKE pattern;
LOOP
FETCH cAuthors INTO rAuthor;
EXIT WHEN cAuthors%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(rAuthor.name);
END LOOP;
CLOSE cAuthors;
EXCEPTION WHEN OTHERS THEN
CLOSE cAuthors;
RAISE;
END;
/
EXEC getAuthors('%e%');
James Blish
Jerry Pournelle
Kurt Vonnegut
Larry Niven
William Shakespeare
PL/SQL procedure successfully completed.
This example Security implementation contains some interesting example cursor coding.
Dynamic SQL is literally dynamic or generic, constructed at run-time, and has the following characteristics.
Let's look at some examples. Count all rows in all tables.
DECLARE CURSOR cTables IS SELECT table_name FROM user_tables ORDER BY table_name; rows INTEGER; BEGIN FOR rTable IN cTables LOOP EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM '||rTable.table_name INTO rows; DBMS_OUTPUT.PUT_LINE(RPAD(rTable.table_name,30,' ')||' = '||TO_CHAR(rows)); END LOOP; END; / AUTHOR = 7 COAUTHOR = 2 CRITIC = 2 EDITION = 18 PUBLICATION = 12 PUBLISHER = 12 REVIEW = 10 REVIEWER = 0 SUBJECT = 17 PL/SQL procedure successfully completed.
Now do the same thing but using a variable.
DECLARE CURSOR cTables IS SELECT table_name FROM user_tables ORDER BY table_name; sqlcode VARCHAR2(4000); rows INTEGER; BEGIN FOR rTable IN cTables LOOP sqlcode := 'SELECT COUNT(*) FROM '||rTable.table_name; EXECUTE IMMEDIATE sqlcode INTO rows; DBMS_OUTPUT.PUT_LINE(RPAD(rTable.table_name,30,' ')||' = '||TO_CHAR(rows)); END LOOP; END; /
Similarly we could do the same thing using SQL*Plus scripting without a procedure.
set termout off echo off feed off trimspool on head off pages 0 timing off; spool count.sql; select 'select count(*),'''||table_name||''' from '||table_name||';' from user_tables order by table_name; spool off; set termout on; spool count.log; @@count.sql; spool off;
Here is a simple example incorporating a DDL command. This example would require explicit privileges.
CREATE OR REPLACE PROCEDURE NewTable(tab IN VARCHAR2) AS id INTEGER; name VARCHAR2(32); sqlcode VARCHAR2(4000); BEGIN EXECUTE IMMEDIATE 'CREATE TABLE '||tab||'(id NUMBER, name VARCHAR2(32))'; FOR id IN 1..10 LOOP sqlcode := 'INSERT INTO '||tab||' VALUES(:1, :2)'; name := TO_CHAR(id); EXECUTE IMMEDIATE sqlcode USING id, name; END LOOP; COMMIT; EXCEPTION WHEN OTHERS THEN ROLLBACK; DBMS_OUTPUT.PUT_LINE(SQLERRM(SQLCODE)); RAISE; END; /
This procedure uses the DBMS_SQL procedure.
PROCEDURE dynSQL (pSTR IN VARCHAR2 DEFAULT '') AS cCURSOR INTEGER DEFAULT 0; lROWS INTEGER DEFAULT 0; BEGIN DBMS_OUTPUT.PUT_LINE (pSTR); cCURSOR := DBMS_SQL.OPEN_CURSOR; DBMS_SQL.PARSE (cCURSOR, pSTR, DBMS_SQL.NATIVE); lROWS := DBMS_SQL.EXECUTE (cCURSOR); DBMS_SQL.CLOSE_CURSOR (cCURSOR); EXCEPTION WHEN OTHERS THEN IF DBMS_SQL.IS_OPEN (cCURSOR) THEN DBMS_SQL.CLOSE_CURSOR (cCURSOR); ROLLBACK; END IF; DBMS_OUTPUT.PUT_LINE (SQLERRM(SQLCODE)); RAISE; END; /
There are a multitude of built-in Oracle PL/SQL packages for all sorts of applications. Oracle manuals are the best reference.
Identify configuration options that affect PL/SQL performance perhaps these - from OEM PL/SQL utl_file_dir plsql_v2_compatibility plsql_compiler_glags plsql_native_library_dir plsql_native_library_subdir_count remote_dependencies_mode