Download - 21st Century PLSQL

Transcript
Page 1: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 1

21st Century PL/SQL

Breaking out of Oracle7, Oracle8 and Oracle8i

programming ruts

Steven [email protected]

www.oracleplsqlprogramming.com

All non-technical views expressed are those of Steven Feuerstein and do not necessarily (and not likely!) reflect those of Oracle Corporation.

Page 2: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 2

New stuff is good stuff.

Major Oracle10g PL/SQL compiler upgrades– Optimizing compiler, compile-time warnings

Collections– String-based indexes, multi-level collections, high level set

operations for nested tables, table functions, and more. Advanced topics in Dynamic SQL

– Dynamic PL/SQL, method 4 dynamic SQL, when and how to use DBMS_SQL.

Handy new built-in package functionality– Schedule jobs, send email, recompile code– Miscellaneous wonderful capabilities in DBMS_OUTPUT and

DBMS_UTILITY.

Page 3: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 3

"All" about Steven Feuerstein

Bachelor's degree in mathematics (1980), with three computer 101 classes to my name (a self-taught programmer).

Five years with Oracle Corporation (1987 - 1992), with too much time spent helping salespeople sell. Life is too short...

Author/co-author of nine texts on PL/SQL, most notably Oracle PL/SQL Programming

Senior Technology Advisor for Quest Software I live in Chicago with one wife (Veva), two sons (Chris and

Eli), and three cats (Sister, Moshe and Mica).

www.stevenfeuerstein.com

Page 4: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 4

Ten Years of Writing on the Oracle PL/SQL Language

Page 5: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 5

Software used (or recommended)

You can download all my training materials and demonstration scripts from:

– http://oracleplsqlprogramming.com/resources.html Toad and/or SQL Navigator: make sure you've got a top-

notch IDE, otherwise you are wasting lots of time. Ounit and utPLSQL, software for unit testing of PL/SQL code

at www.ounit.com. MasterMind and Set: have fun while keeping

your brain tuned up. Qnxo, active mentoring software: a repository of

reusable and templated code, www.qnxo.com.plsql_ides.txt

Page 6: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 6

Qnxo is..."Quality iN, eXcellence Out"

A searchable, customizable repository for reusable code and templates.– Starter set: "PL/SQL by Feuerstein"– You can create your own toolboxes and libraries.

A flexible code generator that helps you avoid writing tedious, repetitive code.

An error manager for PL/SQL-based applications.

www.qnxo.com

Page 7: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 7

Qnxo is....

NOT an integrated development environment, aka IDE, aka editor, for PL/SQL programming.– It complements Toad, SQL Navigator, PL/SQL Developer,

etc.

NOT needed in order to benefit from this class. – Qnxo contains a repository of examples.– The Qnxo backend reflects my latest (and, I believe, best)

thinking on how to build high quality PL/SQL applications.

NOT free. – There is a 30-day trial version available at www.qnxo.com.

One student will win a one year subscription to Qnxo.

Page 8: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 8

Major Oracle10g PL/SQL compiler upgrades

Optimizing compiler– Recompile in 10g and experience 100%

improvement in performance (results may vary).

Compile-time warnings– Now the PL/SQL compiler tells you more than

simply compilation errors.

Page 9: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 9

Wow! An optimizing compiler!

Yes, the PL/SQL compiler now has the ability to automatically optimize your code.– Possible rearrangements to the code itself (under the covers).

You can choose the level of optimization through the plsql_optimize_level setting:– 2 Most aggressive, maximum possible code transformations,

biggest impact on compile time. [default]– 1 Smaller scale change, less impact on compile times– 0 Pre-10g compilation without optimization

ALTER SESSION SET PLSQL_OPTIMIZE_LEVEL = 1;

10g_optimize_cfl.sql

Oracle10g

Page 10: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 10

Learn about the PL/SQL optimizer

PL/SQL Just Got Faster – Explains the workings of the PL/SQL compiler and runtime system and

shows how major improvements on this scale are indeed possible.

PL/SQL Performance Measurement Harness – Describes a performance experiment whose conclusion is the large

factors quoted above. We’ve provided a downloadable kit to enable you to repeat the experiment yourself.

Freedom, Order, and PL/SQL Optimization – Intended for professional PL/SQL programmers, explores the use and

behavior of the new compiler.

PL/SQL Performance — Debunking the Myths– Re-examines some old notions about PL/SQL performance.

http://www.oracle.com/technology/tech/pl_sql/htdocs/new_in_10gr1.htm

Page 11: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 11

Optimizing compiler details

Oracle retains optimizer settings on a module-by-module basis. – When you recompile a particular module with

non-default settings, the settings will "stick," allowing you to recompile later using REUSE SETTINGS. For example:

and then:

ALTER PROCEDURE bigproc COMPILE PLSQL_OPTIMIZE_LEVEL = 1;

ALTER PROCEDURE bigproc COMPILE REUSE SETTINGS;

Page 12: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 12

Wow! Compile-time warnings!

You can now enable compiler warnings, helping you avoid nuisance issues in your code.– Generally, these are not severe errors, but potential

problems with code structure or performance.

To use compiler warnings, you must turn them on in your session.

[ENABLE | DISABLE | ERROR]:[ALL|SEVERE|INFORMATIONAL|PERFORMANCE|warning_number]

REM To enable all warnings in your session execute:ALTER SESSION SET plsql_warnings = 'enable:all‘;

REM If you want to enable warning message number 06002 and all warnings in REM the performance category, and treat warning 5005 as a "hard" compile error: ALTER SESSION SET plsql_warnings = 'enable:06002', 'enable:performance', 'ERROR:05005';

Oracle10g

Page 13: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 13

Compiler time warnings - example

Check for “unreachable end” code….SQL> CREATE OR REPLACE PROCEDURE unreachable_code IS2 x NUMBER := 10;3 BEGIN4 IF x = 10 THEN5 x := 20;6 ELSE7 x := 100; -- unreachable code8 END IF;9 END unreachable_code;10 / SP2-0804: Procedure created with compilation warnings SQL> show errErrors for PROCEDURE UNREACHABLE_CODE: LINE/COL ERROR-------- -------------------------------------7/7 PLW-06002: Unreachable code

plw*.sql

Page 14: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 14

Useful data dictionary views

ALL_PLSQL_OBJECT_SETTINGS– New to Oracle10g, this view provides information about

the characteristics of a PL/SQL object that can be modified through the ALTER-SET DDL command, such as the optimization level, debug settings and more.

ALL_PROCEDURES– Introduced in Oracle9i, this view lists all functions and

procedures (stand-alone or packaged), along with associated properties, including whether or not a function is pipelined, parallel enabled, or aggregate.

Page 15: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 15

Using the object settings view

Show all the program units that are not fully leveraging code optimization:

Show all objects which have had one or more compile-time warnings disabled:

SELECT owner, name FROM all_plsql_object_settings WHERE plsql_optimize_level IN (1,0);

SELECT owner, NAME, plsql_warnings FROM all_plsql_object_settings WHERE plsql_warnings LIKE '%DISABLE%' AND owner NOT IN ('SYS', 'SYSTEM');

Page 16: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 16

Using the ALL_PROCEDURES view

The following query will show the AUTHID status (DEFINER or CURRENT_USER) for all programs in the specified package.– In general, this view is handy because it does list each of

the individual program units in a package specification.– Previously, this information could only be deduced from

ALL_ARGUMENTS.

SELECT AUTHID , p.object_name program_name , procedure_name subprogram_name FROM all_procedures p, all_objects o WHERE p.owner = o.owner AND p.object_name = o.object_name AND p.object_name LIKE '&1'ORDER BY AUTHID, procedure_name;

program_start_end.ddlpackage_analyzer.*

Page 17: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 17

PL/SQL Collections

Ever wonder why PL/SQL doesn't have good, old-fashioned arrays?

It's a good question, and one that Oracle seems to answer as follows:

Who needs arrays, when you have collections?

Page 18: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 18

What we will cover on collections

Brief review of basic functionality Indexing collections by strings Working with collections of collections Bulk processing with FORALL and BULK

COLLECT Table functions and pipelined functions MULTISET operators for nested tables

Page 19: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 19

What is a collection?

A collection is an "ordered group of elements, all of the same type." (PL/SQL User Guide and Reference)– That's a very general definition; lists, sets, arrays and similar

data structures are all types of collections.– Each element of a collection may be addressed by a unique

subscript, usually an integer but in some cases also a string.– Collections are single-dimensional, but you can create

collections of collections to emulate multi-dimensional structures.

abc def sf q rrr swq...1 2 3 4 22 23

Page 20: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 20

Why use collections?

Emulate bi-directional cursors, which are not yet supported within PL/SQL.

Bypass mutating table restrictions in DB triggers. Avoid many scenarios that produce "Snapshot too

old" and "Rollback segment too small" errors. Using BULK COLLECT and FORALL....

Parallelize execution of PL/SQL functions inside SQL statements. With table functions....

Dramatically improve multi-row querying, inserting, updating and deleting the contents of tables. Combined with BULK COLLECT and FORALL....

Cache data in program memory for faster access. The difference between PGA and SGA....

Page 21: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 21

System Global Area (SGA) of RDBMS Instance

Refresher: PL/SQL in Shared Memory

Shared Pool

Large Pool

Reserved Pool

show_empscalc_totals upd_salaries

Select * from emp

Shared SQL

Pre-parsedUpdate emp Set sal=...

Library cache

Session 1 memory (PGA/UGA)

emp_rec emp%rowtype;tot_tab tottabtype;

Session 2 memory (PGA/UGA)

emp_rec emp%rowtype;tot_tab tottabtype;Session 1

Session 2

mysess.pkgSess2.sql

Page 22: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 22

Three Types of Collections

Associative arrays (aka index-by tables) – Similar to hash tables in other languages, allows you to

access elements via arbitrary subscript values.

Nested tables – Can be defined in PL/SQL and SQL. Use to store large

amounts of persistent data in the column of a table.– Required for some features, such as table functions

Varrays (aka variable size arrays)– Can be defined in PL/SQL and SQL; useful for defining

small lists in columns of relational tables.

Page 23: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 23

About Associative Arrays

Unbounded, practically speaking. – Valid row numbers range from -2,147,483,647 to

2,147,483,647.– This range allows you to employ the row number as an

intelligent key, such as the primary key or unique index value, because AAs also are:

Index values can be integers or strings (Oracle9i R2 and above).

Sparse– Data does not have to be stored in consecutive rows, as is

required in traditional 3GL arrays and VARRAYs. Try to read a row that doesn't exist, and Oracle raises

NO_DATA_FOUND.assoc_array_example.sql

Page 24: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 24

About Nested Tables

No pre-defined limit on a nested table.– Valid row numbers range from 1 to

2,147,483,647.

Is always dense initially, but can become sparse after deletes.

Can be defined as a schema level type and used as a relational table column type.

Part of object model, requiring initialization.

nested_table_example.sql

Page 25: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 25

About Varrays

Has a maximum size, associated with its type. – Can adjust the size at runtime in Oracle10g R2.

Is always dense; you can only remove elements from the end of a varray.

Can be defined as a schema level type and used as a relational table column type.

Part of object model, requiring initialization.

varray_example.sql

Page 26: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 26

Apply PL/SQL Collections

We will take a look at the following applications of PL/SQL collections:– Mutating table trigger error resolution– Caching data in the PGA with collections– Dealing with rollback segment too small and

snapshot too old errors

Then we will explore advanced features of collections.

Page 27: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 27

Rollback segment too small, Snapshot too old? Perhaps collections can help.

Rollback segment too small...– Cause: so many uncommitted changes, the

rollback segment can't handle it all.– Solution: incremental commits. You can do this

with normal DML but also with FORALL DML. Snapshot too old...

– Cause: a cursor is held open too long and Oracle can no longer maintain the snapshot information.

– Solution: open-close cursor, or use BULK COLLECT to retrieve information more rapidly.

forall_incr_commit.sql

Page 28: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 28

Dealing with Mutating Table errors

Row level triggers cannot query from or change the contents of the table to which it is attached; it is "mutating".

But statement level triggers do not have this restriction.

So what are you supposed to do when a row-level operation needs to "touch" that table?

UPDATE row 1

UPDATE row N

UPDATE emp SET sal = 1000

Database triggers can be associated with both the DML statement as a whole and individual rows affected by that statement.

Note: in Oracle8i, you can use autonomous transactions to relax

restrictions associated with queries.

mutating.sql

Statement level

Row level

Page 29: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 29

A Solution Based on Associative Arrays Tables

Since you cannot perform the processing desired in the row-level trigger, you need to defer the action until you get to the statement level.

If you are going to defer the work, you have to remember what you needed to do. – An associative array is an ideal repository for this reminder list.

1st row trigger fires

Nth row trigger fires

Work List(collection)

Statement Trigger

Writes to list

Writes to list

Process datain the list.

mutating_trigger.pkgranking.pkg

Page 30: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 30

FunctionPGA

Data Caching with PL/SQL Tables

First access

Subsequent accesses

PGAFunction

Database

Not in cache;Request datafrom database

Pass Datato Cache

Application

Application Requests Data

Data retrieved from cache Data returned

to application

Application

Application Requests Data

Data returned to application

Data retrieved from cache

DatabaseData found in

cache. Databaseis not needed.

emplu.pkgemplu.tst

Page 31: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 31

New indexing capabilities for associative arrays

Prior to Oracle9iR2, you could only index by BINARY_INTEGER.

You can now define the index on your associative array to be:– Any sub-type derived from BINARY_INTEGER– VARCHAR2(n), where n is between 1 and 32767– %TYPE against a database column that is consistent with

the above rules– A SUBTYPE against any of the above.

This means that you can now index on string values! (and concatenated indexes and...)

Oracle9i Release 2

Page 32: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 32

Examples of New TYPE Variants

All of the following are now valid TYPE declarations in Oracle9i Release 2– You cannot use %TYPE against an INTEGER column,

because INTEGER is not a subtype of BINARY_INTEGER.

DECLARE TYPE array_t1 IS TABLE OF NUMBER INDEX BY BINARY_INTEGER; TYPE array_t2 IS TABLE OF NUMBER INDEX BY PLS_INTEGER; TYPE array_t3 IS TABLE OF NUMBER INDEX BY POSITIVE; TYPE array_t4 IS TABLE OF NUMBER INDEX BY NATURAL; TYPE array_t5 IS TABLE OF NUMBER INDEX BY VARCHAR2(64); TYPE array_t6 IS TABLE OF NUMBER INDEX BY VARCHAR2(32767); TYPE array_t7 IS TABLE OF NUMBER INDEX BY employee.last_name%TYPE; TYPE array_t8 IS TABLE OF NUMBER INDEX BY types_pkg.subtype_t;

Oracle9i Release 2

Page 33: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 33

Working with VARCHAR2-Indexed Collections

Specifying a row via a string takes some getting used to, but if offers some very powerful advantages.

DECLARE TYPE population_type IS TABLE OF NUMBER INDEX BY VARCHAR2(64);

country_population population_type; continent_population population_type;

howmany NUMBER;BEGIN country_population('Greenland') := 100000; country_population('Iceland') := 750000;

howmany := country_population('Greenland');

continent_population('Australia') := 30000000;END;

assoc_array*.sqlassoc_array_perf.tst

Oracle9i Release 2

Page 34: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 34

Rapid Access to Data Via String Keys

One of the most powerful applications of this features is to construct very fast pathways to static data from within PL/SQL programs. – If you are repeatedly querying the same data from the

database, why not cache it in your PGA inside collections?

Emulate the various indexing mechanisms (primary key, unique indexes) with collections.

Demonstration package:assoc_array5.sql

Oracle9i Release 2

Comparison of performance of different approaches:

vocab*.*

Generate a caching package:genaa.sqlgenaa.tst

Page 35: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 35

Multi-level Collections

Oracle9i allows you to create collections of collections, or collections of records that contain collections, or...

Applies to all three types of collections. Two scenarios to be aware of:

– Named collection columns– Anonymous collection columns

Oracle9i

Page 36: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 36

Collections with Named, Multi-level Collections

When a collection is based on a record or object that in turn contains a collection, that collection has a name.

CREATE TYPE vet_visit_t IS OBJECT ( visit_date DATE, reason VARCHAR2 (100));/CREATE TYPE vet_visits_t IS TABLE OF vet_visit_t/CREATE TYPE pet_t IS OBJECT ( tag_no INTEGER, NAME VARCHAR2 (60), petcare vet_visits_t, MEMBER FUNCTION set_tag_no (new_tag_no IN INTEGER) RETURN pet_t);/

Continued...multilevel_collections.sql

Collection nested inside object type

Oracle9i

Page 37: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 37

Collections with Named, Multi-level Collections, continued

DECLARE TYPE bunch_of_pets_t IS TABLE OF pet_t INDEX BY BINARY_INTEGER; my_pets bunch_of_pets_t;BEGIN my_pets (1) := pet_t ( 100, 'Mercury', vet_visits_t ( vet_visit_t ( '01-Jan-2001', 'Clip wings'), vet_visit_t ( '01-Apr-2002', 'Check cholesterol') ) ); DBMS_OUTPUT.put_line (my_pets (1).petcare (2).reason);END;

Outer collection

Inner collection

Oracle9i

Page 38: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 38

Anonymous Collection Columns

If your nested collections do not rely on "intermediate" records or objects, you simply string together index subscripts.– To demonstrate this syntax, let's take a look at how to

emulate a three-dimensional array using nested collections. First, we cannot directly reference or populate an

individual cell, as one would do in a 3GL.– Instead we have to build an interface between the underlying

arrays and the user of the "three dimensional array."

Oracle9i

BEGIN gps_info (1, 45, 605) := l_value;

BEGIN gps_info (605) (45) (1) := l_value;

Can't do this... Have to do something like this instead...

Page 39: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 39

Multi-dimensional array emulationCREATE OR REPLACE PACKAGE multdim IS TYPE dim1_t IS TABLE OF VARCHAR2 (32767) INDEX BY BINARY_INTEGER;  TYPE dim2_t IS TABLE OF dim1_t INDEX BY BINARY_INTEGER;  TYPE dim3_t IS TABLE OF dim2_t INDEX BY BINARY_INTEGER;  PROCEDURE setcell ( array_in IN OUT dim3_t, dim1_in PLS_INTEGER, dim2_in PLS_INTEGER, dim3_in PLS_INTEGER, value_in IN VARCHAR2 );  FUNCTION getcell ( array_in IN dim3_t, dim1_in PLS_INTEGER, dim2_in PLS_INTEGER, dim3_in PLS_INTEGER ) RETURN VARCHAR2;END multdim;

multdim.*multdim2.*

gen_multcoll.sp

Oracle9i

Three levels of collections

Set a cell value

Get a cell value

Page 40: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 40

Multi-dimensional array emulation

CREATE OR REPLACE PACKAGE BODY multdimIS PROCEDURE setcell ( array_in IN OUT dim3_t, dim1_in PLS_INTEGER, dim2_in PLS_INTEGER, dim3_in PLS_INTEGER, value_in IN VARCHAR2 ) IS BEGIN array_in(dim3_in )(dim2_in )(dim1_in) := value_in; END;  FUNCTION getcell ( array_in IN dim3_t, dim1_in PLS_INTEGER, dim2_in PLS_INTEGER, dim3_in PLS_INTEGER) RETURN VARCHAR2 IS BEGIN RETURN array_in(dim3_in )(dim2_in )(dim1_in); END;

Oracle9i

As close as you can get...

Page 41: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 41

Applying multi-level and string-based indexes

Careful -- and creative! -- application of this functionality can greatly simplify the code you need to write to handle complex requirements.

Let's step through an application of this capability to a programming challenge.

overloadings

naming_conventions

bad_datatypes

CodecheckpackageOverloadCheck:

A QA packagefor PL/SQL

Page 42: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 42

The problem of ambiguous package overloadings

Oddly and sadly, it is possible to compile overloadings which are not usable.– You see an obvious example below, but there are many

more subtle circumstances, usually involving defaulted parameters.

So I will build a program to identify such ambiguous overloadings. But how can I do this?

BEGIN salespkg.calc_total ('ABC');END;/

PACKAGE salespkgIS PROCEDURE calc_total ( dept_in IN VARCHAR2); PROCEDURE calc_total ( dept_in IN CHAR);END salespkg;

?ambig_overloading.sql

Page 43: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 43

ALL_ARGUMENTS to the rescue!

Parsing is too complicated for me, but the ALL_ARGUMENTS data dictionary view contains information about all the arguments of all the procedures and functions to which I have access. That sounds pretty good!

As usual, Oracle offers us a whole lot of pleasure, mixed with a little bit of pain.– The organization of data in ALL_ARGUMENTS is a bit

bizarre, plus it is incomplete, necessitating the use also of DBMS_DESCRIBE.DESCRIBE_COLUMNS.

all_arguments.tstall_arguments.sql

allargs.*

Page 44: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 44

First Inclination: Same Old, Same Old

All right then, I will grab all the information from ALL_ARGUMENTS and dump it into a collection based on that view! Very easy...

CREATE OR REPLACE PROCEDURE get_all_arguments ( package_in IN VARCHAR2)IS TYPE all_arguments_tt IS TABLE OF all_arguments%ROWTYPE INDEX BY BINARY_INTEGER; l_arguments all_arguments_tt;BEGIN FOR rec IN ( SELECT * FROM all_arguments WHERE owner = USER AND package_name = package_in) LOOP l_arguments (SQL%ROWCOUNT) := rec; END LOOP;END;

Load it up!

Emulate the view.

Page 45: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 45

Then what? Write lots of code to interpret the contents...

Which programs are overloaded? Where does one overloading end and another start?

l_last_program all_arguments.object_name%TYPE; l_is_new_program BOOLEAN := FALSE; l_last_overload PLS_INTEGER := -1;BEGIN FOR indx IN l_arguments.FIRST .. l_arguments.LAST LOOP IF l_arguments (indx).object_name != l_last_program OR l_last_program IS NULL THEN l_last_program := l_arguments (indx).object_name; l_is_new_program := TRUE; do_new_program_stuff; END IF; ...

IF l_arguments (indx).overload != l_last_overload OR l_last_overload = -1 THEN IF l_is_new_program THEN do_first_overloading_stuff; ELSE do_new_overloading_stuff; END IF; END IF; END LOOP;END;

Page 46: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 46

Discovery: there is a natural hierarchy to ALL_ARGUMENTS data!

Each program has zero or more overloadings, each overloading has N arguments, and each argument can have multiple "breakouts" (my term - applies to non-scalar parameters, such as records or object types).

RUN_TEST

SHOW_RESULTS

RESET_FLAGS

Program name

Overloading 1

Overloading 2

Overloading

Argument 1

Argument 2

Argument 3

Argument 4

Argument 5

ArgumentBreakout 1

Breakout 1

Breakout 2

Breakout 3

Breakout

Page 47: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 47

What if I reflect this hierarchy in a collection of collections?

Have to build from the bottom up:

TYPE breakouts_t IS TABLE OF all_arguments%ROWTYPE INDEX BY BINARY_INTEGER;

TYPE arguments_t IS TABLE OF breakouts_t INDEX BY BINARY_INTEGER;

TYPE overloadings_t IS TABLE OF arguments_t INDEX BY BINARY_INTEGER;

TYPE programs_t IS TABLE OF overloadings_t INDEX BY all_arguments.object_name%type;

1. Set of rows from ALL_ARGUMENTS

String-based index

2. All the "breakout" info for a single argument

3. All the argument info for a single overloading

4. All the overloadings for a distinct program name

Page 48: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 48

Then I can populate it very easily

Assigning a single record to the "lowest" level also defines each of the upper levels.

Notice the automatic "SELECT DISTINCT" on program name that results!

FOR rec IN (SELECT * FROM all_arguments)LOOP l_arguments (NVL (l_arguments.LAST, 0) + 1) := rec;  l_programs (rec.object_name) (NVL (rec.overload, 0)) (rec.position) (rec.data_level) := rec;END LOOP;

I can still do the typical sequential

load.

But I will now also add the multi-level

load in single assignment

show_all_arguments.spshow_all_arguments.tst

Page 49: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 49

And then I can "query" the contents with a minimum of code

l_programs ('TOP_SALES') (2).EXISTS (0)

Is the TOP_SALES program overloaded?

l_programs ('TOP_SALES') (2)(0)(0).datatype

l_programs ('TOP_SALES').COUNT > 1

Is the 2nd overloading of TOP_SALES a

function?

What is the datatype of the RETURN clause of the 2nd overloading

of TOP_SALES?

And, of course, I know the beginning and end points of each program, overloading, and argument. I just use the

FIRST and LAST methods on those collections!

Page 50: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 50

Encapsulate these complex structures!

As you can see, you can easily and rapidly arrive at completely unreadable and un-maintainable code.

What' s a developer to do?– Hide complexity -- and all data structures -- behind

small modules.– Work with and through tunctions to retrieve

contents and procedures to set contents.

cc_smartargs.pkb:cc_smartargs.next_overloading

cc_smartargs.add_new_parameter

Page 51: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 51

Nested Tables unveil their MULTISET-edness

Oracle10g introduces high-level set operations on nested tables (only).– Nested tables are “multisets,” meaning that theoretically

there is no order to their elements. This makes set operations of critical importance for manipulating nested tables. .

You can now…– Check for equality and inequality– Obtain UNION, INTERSECT and MINUS of two NTs– Determine if there are duplicates, remove them, etc.

Oracle10g

Page 52: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 52

Check for equality and inequality

Just use the basic operators….

Oracle10g

DECLARE TYPE clientele IS TABLE OF VARCHAR2 (64); group1 clientele := clientele ('Customer 1', 'Customer 2'); group2 clientele := clientele ('Customer 1', 'Customer 3'); group3 clientele := clientele ('Customer 3', 'Customer 1');BEGIN IF group1 = group2 THEN DBMS_OUTPUT.put_line ('Group 1 = Group 2'); ELSE DBMS_OUTPUT.put_line ('Group 1 != Group 2'); END IF;

IF group2 != group3 THEN DBMS_OUTPUT.put_line ('Group 2 != Group 3'); ELSE DBMS_OUTPUT.put_line ('Group 2 = Group 3'); END IF;END;

10g_compare.sql10g_compare2.sql

10g_compare_old.sql

Page 53: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 53

UNION, INTERSECT, MINUS

Straightforward, with the MULTISET keyword.

Oracle10g

BEGIN our_favorites := my_favorites MULTISET UNION dad_favorites; show_favorites ('MINE then DAD', our_favorites); our_favorites := dad_favorites MULTISET UNION my_favorites; show_favorites ('DAD then MINE', our_favorites); our_favorites := my_favorites MULTISET UNION DISTINCT dad_favorites; show_favorites ('MINE then DAD with DISTINCT', our_favorites); our_favorites := my_favorites MULTISET INTERSECT dad_favorites; show_favorites ('IN COMMON', our_favorites); our_favorites := dad_favorites MULTISET EXCEPT my_favorites; show_favorites ('ONLY DAD''S', our_favorites); END;

10g_setops.sql10g_string_nt.sql10g_favorites.sql

10g*union*.sql

Page 54: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 54

Distinct sets of values

Use the SET operator to work with distinct values, and determine if you have a set of distinct values.

Oracle10g

DECLARE keep_it_simple strings_nt := strings_nt ();BEGIN keep_it_simple := SET (favorites_pkg.my_favorites);

favorites_pkg.show_favorites ('FULL SET', favorites_pkg.my_favorites);

p.l (favorites_pkg.my_favorites IS A SET, 'My favorites distinct?'); p.l (favorites_pkg.my_favorites IS NOT A SET, 'My favorites NOT distinct?'); favorites_pkg.show_favorites ( 'DISTINCT SET', keep_it_simple); p.l (keep_it_simple IS A SET, 'Keep_it_simple distinct?'); p.l (keep_it_simple IS NOT A SET, 'Keep_it_simple NOT distinct?');

END;

10g_set.sql10g_favorites.pkg

Page 55: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 55

Determining subsets of data

Use the SUBMULTISET operator to determine if a nested table contains only elements that are in another nested table.

Oracle10g

BEGIN p.l (favorites_pkg.my_favorites SUBMULTISET OF favorites_pkg.eli_favorites , 'Father follows son?');

p.l (favorites_pkg.eli_favorites SUBMULTISET OF favorites_pkg.my_favorites , 'Son follows father?');

p.l (favorites_pkg.my_favorites NOT SUBMULTISET OF favorites_pkg.eli_favorites , 'Father doesn''t follow son?');

p.l (favorites_pkg.eli_favorites NOT SUBMULTISET OF favorites_pkg.my_favorites , 'Son doesn''t follow father?');END; 10g_submultiset.sql

10g_favorites.pkg

Page 56: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 56

SQL on Steroids: Bulk Processing

Oracle8i and Oracle9i offer groundbreaking new syntax to improve the performance of both DML and queries.

In Oracle8, updating from a collection (or, in general, performing multi-row DML) meant writing code like this:

CREATE TYPE dlist_t AS TABLE OF INTEGER;/PROCEDURE remove_emps_by_dept (deptlist dlist_t)ISBEGIN FOR aDept IN deptlist.FIRST..deptlist.LAST LOOP DELETE emp WHERE deptno = deptlist(aDept); END LOOP;END;

“Conventional binds” (and lots of them!)

Oracle8iOracle9i

Page 57: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 57

Oracle server

PL/SQL Runtime Engine SQL Engine

PL/SQL blockProcedural statement executor

SQL statement executor

FOR aDept IN deptlist.FIRST.. deptlist.LASTLOOP DELETE emp WHERE deptno = deptlist(aDept);END LOOP;

Performance penalty Performance penalty for many “context for many “context switches”switches”

Conventional Bind

Page 58: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 58

Enter the “Bulk Bind”

Oracle server

PL/SQL Runtime Engine SQL Engine

PL/SQL blockProcedural statement executor

SQL statement executor

FORALL aDept IN deptlist.FIRST.. deptlist.LAST DELETE emp WHERE deptno = deptlist(aDept);

Much less overhead for Much less overhead for context switchingcontext switching

Page 59: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 59

Use the FORALL Bulk Bind Statement

Instead of the individual DML operations, you can do this:

Things to be aware of:– Only a single DML statement is allowed. If you want to

INSERT and then UPDATE, two different FORALL statements.

– SQL%BULK_ROWCOUNT returns the number of rows affected by each row in the binding array.

PROCEDURE remove_emps_by_dept (deptlist dlist_t)ISBEGIN FORALL aDept IN deptlist.FIRST..deptlist.LAST DELETE FROM emp WHERE deptno = deptlist(aDept);END;

Page 60: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 60

CREATE OR REPLACE PROCEDURE process_emps (deptno_in IN dept.depno%TYPE)IS TYPE three_cols_rt IS RECORD ( empno emp.empno%TYPE, ename emp.ename%TYPE, hiredate emp.hiredate%TYPE); TYPE three_cols_tt IS TABLE OF three_cols_rt INDEX BY PLS_INTEGER; three_cols_t three_cols_tt;BEGIN SELECT empno, ename, hiredate BULK COLLECT INTO three_cols_t FROM emp WHERE deptno = deptno_in; ...END;

Use BULK COLLECT INTO for Queries

CREATE OR REPLACE PROCEDURE process_emps (deptno_in IN dept.depno%TYPE)IS TYPE numTab IS TABLE OF NUMBER; TYPE charTab IS TABLE OF VARCHAR2(12); TYPE dateTab IS TABLE OF DATE; enos numTab; names charTab; hdates dateTab;BEGIN SELECT empno, ename, hiredate BULK COLLECT INTO enos, names, hdates FROM emp WHERE deptno = deptno_in; FOR i IN enos.FIRST..enos.LAST LOOP do_stuff (enos(i), names(i), hiredates(i)); END LOOP;END;

Oracle9i R2 supports fetching into a collection of

records.

Oracle8i requires fetching into individual collections of

scalars.

bulkcoll.sql

Page 61: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 61

Limit the number of rows returned by BULK COLLECT

CREATE OR REPLACE PROCEDURE bulk_with_limit (deptno_in IN dept.deptno%TYPE)IS CURSOR emps_in_dept_cur IS SELECT * FROM emp WHERE deptno = deptno_in;

TYPE emp_tt IS TABLE OF emp%ROWTYPE; emps emp_tt;BEGIN OPEN three_cols_cur; LOOP FETCH emps_in_dept_cur BULK COLLECT INTO emps LIMIT 100;

EXIT WHEN emps.COUNT = 0;

process_emps (emps); END LOOP;END bulk_with_limit;

Use the LIMIT clause with the INTO to manage the amount

of memory used with the BULK COLLECT operation.

WARNING!

BULK COLLECT will not raise NO_DATA_FOUND if no rows

are found.

Best to check contents of collection to confirm that something was retrieved.

bulklimit.sql

Page 62: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 62

Combining FORALL & BULK COLLECT

FUNCTION remove_emps_by_dept (deptlist dlist_t) RETURN enolist_tIS enolist enolist_t;BEGIN FORALL aDept IN deptlist.FIRST..deptlist.LAST DELETE FROM emp WHERE deptno IN deptlist(aDept) RETURNING empno BULK COLLECT INTO enolist; RETURN enolist;END;

Use the RETURNING clause to obtain information about each DML statement executed with FORALL– When executing multiple DML statements, you need to BULK

COLLECT the RETURNING results into one or more collections

– SQL%BULK_ROWCOUNT is a pseudo-collection containing the numbers of rows modified by each statement.

bulk_rowcount.sql

Page 63: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 63

Tips and Fine Points

Use bulk binds in these circumstances:– Recurring SQL statement in PL/SQL loop– Use of a collection as the bind variable, or code that could

be transformed to use a collection containing the bind variable information

Bulk bind rules:– Can be used with any kind of collection; Collection

subscripts cannot be expressions; The collections must be densely filled (pre-10g); If error occurs, prior successful DML statements are NOT ROLLED BACK

Bulk collects: – Can be used with implicit and explicit cursors

Collection is filled starting at row 1bulktiming.sql

Page 64: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 64

Oracle9i Enhancements

You can now use dynamic SQL strings in the bulk bind and collect statements.– FORALL for bulk DML– BULK COLLECT for bulk queries.

This gives you virtually unlimited flexibility without a tradeoff in performance.

Oracle9i

Page 65: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 65

Dynamic FORALL Example

This example shows the use of bulk binding and collecting, plus application of the RETURNING clause.

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) IS enames NameList;BEGIN FORALL indx IN empnos_in.FIRST .. empnos_in.LAST EXECUTE IMMEDIATE 'UPDATE emp SET ' || col_in || ' = ' || col_in || ' * 1.1 WHERE empno = :1 RETURNING ename INTO :2' USING empnos_in (indx ) RETURNING BULK COLLECT INTO enames; ...END;

Notice that empnos_in is indexed, but enames

is not.

Oracle9i

Page 66: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 66

Dynamic BULK COLLECT

Now you can even avoid the OPEN FOR and just grab 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 employee.name%TYPE; TYPE employee_t IS TABLE OF employee%ROWTYPE;

emp_cv sys_refcursor;

empnos numlist_t; enames namelist_t; l_employees employee_t;BEGIN OPEN emp_cv FOR 'SELECT empno, ename FROM emp_' || loc_in; FETCH emp_cv BULK COLLECT INTO empnos, enames; CLOSE emp_cv;

EXECUTE IMMEDIATE 'SELECT * FROM emp_' || loc_in BULK COLLECT INTO l_employees;END;

With Oracle9iR2 you can also fetch into collections of

records.

Oracle9i

Page 67: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 67

Better Exception Handlingfor Bulk Operations

Allows you to continue past errors and obtain error information for each individual operation (for dynamic and static SQL).

CREATE OR REPLACE PROCEDURE load_books (books_in IN book_obj_list_t)IS bulk_errors EXCEPTION; PRAGMA EXCEPTION_INIT ( bulk_errors, -24381 );BEGIN FORALL indx IN books_in.FIRST..books_in.LAST SAVE EXCEPTIONS INSERT INTO book values (books_in(indx));EXCEPTION WHEN BULK_ERRORS THEN FOR indx in 1..SQL%BULK_EXCEPTIONS.COUNT LOOP log_error (SQL%BULK_EXCEPTIONS(indx)); END LOOP;END;

Allows processing of all rows, even after an error

occurs.

New cursor attribute, a pseudo-

collection

bulkexc.sql

Oracle9i

Page 68: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 68

Cursor FOR Loop ... or BULK COLLECT?

Why would you ever use a cursor FOR loop (or other LOOP) now that you can perform a BULK COLLECT?– If you want to do complex processing on each

row as it is queried – and possibly halt further fetching.

– You are retrieving many rows and cannot afford to use up the memory (large numbers of users).

Otherwise, moving to BULK COLLECT is a smart move!

cfl_vs_bulkcollect.sqlcfl_to_bulk.sql

Page 69: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 69

More flexibility with FORALL

In Oracle10g, the FORALL driving array no longer needs to be processed sequentially.

Use the INDICES OF clause to use only the row numbers defined in another array.

Use the VALUES OF clause to use only the values defined in another array.

Oracle10g

Page 70: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 70

Using INDICES OF

It only processes the rows with row numbers matching the defined rows of the driving array.

Oracle10g

10g_indices_of.sql10g_indices_of2.sql

DECLARE TYPE 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;BEGIN l_employees (1) := 7839; l_employees (100) := 7654; l_employees (500) := 7950; -- l_employee_indices (1) := TRUE; l_employee_indices (500) := TRUE; l_employee_indices (799) := TRUE -- FORALL l_index IN INDICES OF l_employee_indices BETWEEN 1 AND 500 UPDATE employee SET salary = 10000 WHERE employee_id = l_employees (l_index);END;

Page 71: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 71

Using VALUES OF

It only processes the rows with row numbers matching the content of a row in the driving array.

Oracle10g

DECLARE TYPE employee_aat IS TABLE OF employee.employee_id%TYPE INDEX BY PLS_INTEGER; l_employees employee_aat;

TYPE values_aat IS TABLE OF PLS_INTEGER INDEX BY PLS_INTEGER; l_employee_values values_aat;BEGIN l_employees (-77) := 7820; l_employees (13067) := 7799; l_employees (99999999) := 7369; -- l_employee_values (100) := -77; l_employee_values (200) := 99999999; -- FORALL l_index IN VALUES OF l_employee_values UPDATE employee SET salary = 10000 WHERE employee_id = l_employees (l_index);END;

10g_values_of.sql

Page 72: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 72

Advanced topics in Dynamic SQL

Dynamic PL/SQL block execution Method 4 dynamic SQL When DBMS_SQL comes in handy...

– Parse very long strings– Describe columns in dynamic query– And method 4...

First, a brief overview.

Page 73: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 73

What is Dynamic SQL?

Dynamic SQL actually refers, in the world of PL/SQL, to two things:– SQL statements, such as a DELETE or CREATE

TABLE, that are constructed and executed at run-time.

– Anonymous PL/SQL blocks that are constructed, compiled and executed at run-time.

'DROP ' || l_type || ' ' || l_name

'BEGIN ' || l_proc_name || ' (' || l_parameters || '); END;'

Page 74: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 74

Some of the possibilities with Dynamic SQL

Build ad-hoc query and update applications.– The user decides what to do and see.

Execute DDL statements from within PL/SQL.– Not otherwise allowed in a PL/SQL block.

Soft-code your application logic, placing business rules in tables and executing them dynamically.– Usually implemented through dynamic PL/SQL

Page 75: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 75

Two Methods Available

DBMS_SQL– A large and complex built-in package that made

dynamic SQL possible in Oracle7 and Oracle8.

Native Dynamic SQL– A new (with Oracle8i), native implementation of

dynamic SQL that does almost all of what DBMS_SQL can do, but much more easily and usually more efficiently.

Which should you use?

Oracle8i

Page 76: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 76

NDS or DBMS_SQL: Which is best?

Reasons to go with NDS:– Ease of use– Performance (usually

faster)– Works with all SQL

datatypes (including user-defined object and collection types)

– Fetch into records and collections of records

Why You'd Use DBMS_SQL:– Method 4 Dynamic SQL– DESCRIBE columns of

cursor– SQL statements larger than

32K– Better reuse of parsed SQL

statements -- persistent cursor handles!

– Available from client-side PL/SQL

Bottom line: NDS should be your first choice.

Page 77: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 77

Some examples of "typical" dynamic SQL

Dynamic DDL– Simply execute a string; no

information is returned or "bound"

Dynamic queries– Single and multiple rows

Dynamic DML– You will often want to bind in

variable values

dropwhatever.spcreind81.spsettrig.sp

tabcount81.sftabcount.sfshowcol*.sp

updnval_with_eh.sp

Page 78: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 78

Quiz!

PROCEDURE process_lineitem ( line_in IN INTEGER)ISBEGIN IF line_in = 1 THEN process_line1; END IF;  IF line_in = 2 THEN process_line2; END IF;  ... IF line_in = 22045 THEN process_line22045; END IF;END;

What's wrong with this code?

How would you fix it?

Page 79: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 79

From 22,000 lines of code to 1!

Identify the pattern and resolve it either with reusable modules or dynamic abstractions.

PROCEDURE process_lineitem ( line_in IN INTEGER)ISBEGIN IF line_in = 1 THEN process_line1; END IF;  IF line_in = 2 THEN process_line2; END IF;  ... IF line_in = 22045 THEN process_line22045; END IF;END;

PROCEDURE process_lineitem ( line_in IN INTEGER)ISBEGIN EXECUTE IMMEDIATE 'BEGIN process_line'|| line_in ||'; END;';END;

dynplsql.txt

Page 80: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 80

Dynamic PL/SQL with NDS

Dynamically construct, compile and run an anonymous block with EXEC. IMMEDIATE.– Example: DBMS_JOB uses dynamic PL/SQL to run

stored procedures at scheduled times.

Oracle8i

DECLARE v_jobno INTEGER;BEGIN DBMS_JOB.submit ( job => v_jobno, what => 'DBMS_DDL.ANALYZE_OBJECT ' || '(''TABLE'',''LOAD1'',''TENK''' || ',''ESTIMATE'',null,estimate_percent=>50);', next_date => TRUNC (SYSDATE + 1), interval => 'TRUNC(SYSDATE+1)' );END;

Page 81: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 81

Rules for Dynamic PL/SQL

You must construct and execute a valid anonymous block.– Begins with BEGIN or DECLARE.– Ends with END;. The trailing semi-colon is required;

otherwise it is parsed as dynamic SQL. You can only reference globally-accessible data

structures (declared in a package specification). Exceptions can (and should) be trapped in the

block from which the dynamic PL/SQL was executed.

dynplsql8i.spdynplsql_nolocal.sql

Page 82: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 82

Dynamic PL/SQL Possibilities

There are so many possibilities....some things I have done:– Dramatically reduce code volume, improve performance.– Generic string parsing engine: parse any string into your

own collection.– Generic calculator engine.– Implement support for "indirect referencing": read and

change values of variables whose names are only determined at run-time.

– Build a unit test harness to automatically find and run your test code.

And there are also dangers: code injectiondynvar81.pkgdyncalc.pkg

code_injection.sql

Page 83: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 83

The utPLSQL Architecture

utPLSQL.test constructs the names of and executes the setup, unit test and teardown procedures based on the package name you provide.

ut_Setup

ut_TestMe

ut_Teardown

utPLSQL.testTest Engine

Your Test Package

Results Table

Test for Nulls

Invalid ID

Valid ID

Start date too late

End date too early

Name unique

Report Results

1

2

34

Assert EQ

Assert NULL

Assert EqTable

Assertion API

5

http://utplsql.sourceforge.net/http://utplsql.oracledeveloper.nl/

Page 84: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 84

How to build dynamic PL/SQL code

1. Build a static version of the logic you want to execute dynamically.– Test it thoroughly.

2. Identify the portions of the static code which will need to be made dynamic.

3. Convert the block, concatenating or binding those portions which are now dynamic.

4. Include tight error handling and reporting from the start to make sure you can quickly identify and fix issues.

Page 85: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 85

1. Write and verify the static block code.

Here is a static program to parse a string of directories for the path list.

PROCEDURE setpath (str IN VARCHAR2, delim IN VARCHAR2 := c_delim)IS v_loc PLS_INTEGER; v_startloc PLS_INTEGER := 1; v_item VARCHAR2 (2000);BEGIN dirs.DELETE; LOOP v_loc := INSTR (str, delim, v_startloc);

IF v_loc = v_startloc THEN v_item := NULL; ELSIF v_loc = 0 THEN v_item := SUBSTR (str, v_startloc); ELSE v_item := SUBSTR (str, v_startloc, v_loc - v_startloc); END IF;

dirs (dirs.COUNT + 1) := v_item;

IF v_loc = 0 THEN EXIT; ELSE v_startloc := v_loc + 1; END IF; END LOOP;END set_path;

filepath.pkg

Page 86: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 86

2. Identify the dynamic elements of the block.

PROCEDURE setpath (str IN VARCHAR2, delim IN VARCHAR2 := c_delim)IS v_loc PLS_INTEGER; v_startloc PLS_INTEGER := 1; v_item VARCHAR2 (2000);BEGIN dirs.DELETE; LOOP v_loc := INSTR (str, delim, v_startloc);

IF v_loc = v_startloc THEN v_item := NULL; ELSIF v_loc = 0 THEN v_item := SUBSTR (str, v_startloc); ELSE v_item := SUBSTR (str, v_startloc, v_loc - v_startloc); END IF;

dirs (dirs.COUNT + 1) := v_item;

IF v_loc = 0 THEN EXIT; ELSE v_startloc := v_loc + 1; END IF; END LOOP;END set_path;

Dynamic code

Bind variable

Page 87: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 87

3a. Convert from static to dynamic block.

Assign the complex string to a variable.

Makes it easier to report errors and debug.

dynblock := 'DECLARE v_loc PLS_INTEGER; v_start PLS_INTEGER := 1; v_item ' || datatype || '; BEGIN ' || collname || '.DELETE; IF :str IS NOT NULL THEN LOOP v_loc := INSTR (:str, :delim, v_start); IF v_loc = v_startloc THEN v_item := NULL; ELSIF v_loc = 0 THEN v_item := SUBSTR (:str, v_start); ELSE v_item := SUBSTR (:str, v_start, v_loc - v_start); END IF;' || collname || '(' || nextrowstring || ') := v_item; IF v_loc = 0 THEN EXIT; ELSE v_start := v_loc + 1; END IF; END LOOP; END IF; END;';

str2list.pkg

Page 88: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 88

3b. Execute the dynamic block.

With dynamic PL/SQL, even if you reference the same bind variable more than once, you only specify it once in the USING clause.– In other words, PL/SQL is using a variation of

"named notation" rather than the default positional notation for dynamic SQL statements.

EXECUTE IMMEDIATE dynblock USING str, delim;

Page 89: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 89

4. Build in solid error handling from the start.

In your error handler, consider calling these functions to provide useful information:

EXCEPTION WHEN OTHERS THEN disperr (dynblock); RAISE; END parse;

PROCEDURE disperr (str IN VARCHAR2)ISBEGIN DBMS_OUTPUT.PUT_LINE ('Compilation/Execution Error:'); DBMS_OUTPUT.PUT_LINE (DBMS_UTILITY.FORMAT_ERROR_STACK); DBMS_OUTPUT.PUT_LINE ('In:'); DBMS_OUTPUT.PUT_LINE (str); DBMS_OUTPUT.PUT_LINE ('Backtrack Error Stack:'); DBMS_OUTPUT.PUT_LINE (DBMS_UTILITY.FORMAT_ERROR_BACKTRACE); DBMS_OUTPUT.PUT_LINE ('Call Stack:'); DBMS_OUTPUT.PUT_LINE (DBMS_UTILITY.FORMAT_CALL_STACK);END;

Display full error message.

Show line number on which error was

raised.Oracle10g only.

Show execution call stack.

Page 90: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 90

Method 4 Dynamic SQL with DBMS_SQL and NDS

Method 4 dynamic SQL is the most generalized and most complex - by far!– You don't know at compile time either the number of

columns or the number of bind variables.– With DBMS_SQL, you must put calls to

DBMS_SQL.DEFINE_COLUMN and/or DBMS_SQL.BIND_VARIABLE into loops.

With NDS, you must shift from dynamic SQL to dynamic PL/SQL.– How else can you have a variable INTO or USING

clause?

Page 91: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 91

Dynamic "SELECT * FROM <table>" in PL/SQL

You provide the table and WHERE clause. I display all the data.– I don't know in advance which or how many rows

to query.

I can obtain the column information from ALL_TAB_COLUMNS...and from there the fun begins!

Let's compare the DBMS_SQL and NDS implementations.

intab.spintab9i.spintab9i.tst

Page 92: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 92

Pseudo-code flow for DBMS_SQL implementation

BEGIN FOR each-column-in-table LOOP add-column-to-select-list; END LOOP;

DBMS_SQL.PARSE (cur, select_string, DBMS_SQL.NATIVE);

FOR each-column-in-table LOOP DBMS_SQL.DEFINE_COLUMN (cur, nth_col, datatype); END LOOP;

fdbk := DBMS_SQL.EXECUTE (cur); LOOP fetch-a-row; FOR each-column-in-table LOOP DBMS_SQL.COLUMN_VALUE (cur, nth_col, val); END LOOP; END LOOP;END;

Build the SELECT list

Define each column

Extract each value

Parse the variable SQL

Execute the query

Lots of code, but relatively straightforwardintab.sp

Page 93: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 93

The method 4 challenge for NDS

It's not too difficult to build the SELECT list (I can even use BULK COLLECT against ALL_TAB_COLUMNS to speed up the process).

But I still have to fetch that data back into local variables. How many? Who knows! And the INTO clause is static.

EXECUTE IMMEDIATE 'SELECT ' || col_list || ' FROM ' || table_in || ' WHERE ' || where_clauseINTO col1, col2 ... ?

Thiswon'twork:

EXECUTE IMMEDIATE 'SELECT ' || col_list || ' FROM ' || table_in || ' WHERE ' || where_clauseINTO || into_list;

How about this?

It looks like, it must be...dynamic PL/SQL!

Page 94: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 94

Shifting paradigms to dynamic PL/SQL

So now I am thinking about building the entire block to fetch from and display the data. – In which case, I can put static SQL inside my dynamic PL/SQL.

BEGIN l_block := 'BEGIN FOR rec IN (' || query_string || ') LOOP DBMS_OUTPUT.PUT_LINE (' || concatenated_values || '); END LOOP; END;'; EXECUTE IMMEDIATE l_block;END;

No need for INTO or USING clauses

Construct a static query inside a CFL

Concatenate all retrieved values into single string

expression.

It's all one big PL/SQL block

intab9i.sp

Page 95: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 95

Parsing very long strings

One problem with EXECUTE IMMEDIATE is that you pass it a single VARCHAR2 string. – Maximum length 32K.

So what do you do when your string is longer?– Very likely to happen when you are generating SQL

statements based on tables with many columns.– Also when you want to dynamically compile a

program.

Time to switch to DBMS_SQL!

Page 96: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 96

DBMS_SQL.PARSE overloading for collections

Oracle offers an overloading of DBMS_SQL.PARSE that accepts a collection of strings, rather than a single string.

DBMS_SQL offers two different array types:– DBMS_SQL.VARCHAR2S - each string max 255

bytes.– DBMS_SQL.VARCHAR2A - each string max

32,767 bytes (new in Oracle9i).

Page 97: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 97

Compile from a file with DBMS_SQL

CREATE OR REPLACE PROCEDURE compile_from_file ( dir_in IN VARCHAR2 , file_in IN VARCHAR2)IS l_file UTL_FILE.file_type; l_lines DBMS_SQL.varchar2s; l_cur PLS_INTEGER := DBMS_SQL.open_cursor;

PROCEDURE read_file (lines_out IN OUT DBMS_SQL.varchar2s) IS BEGIN ... Read each line into array; see compile_from_file.sql l_file := UTL_FILE.fopen (dir_in, file_in, 'R'); END read_file;BEGIN read_file (l_lines); DBMS_SQL.parse (l_cur , l_lines , l_lines.FIRST , l_lines.LAST , TRUE , DBMS_SQL.native ); DBMS_SQL.close_cursor (l_cur); compile_from_file.sql

You can specify a subset of lines in

the array.

Page 98: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 98

Describe columns in a query

DBMS_SQL offers the ability to "ask" a cursor to describe the columns defined in that cursor.

By using the DESCRIBE_COLUMNS procedure, you can sometimes avoid complex parsing and analysis logic. – Particularly useful with method 4 dynamic SQL.

desccols.pkgdesccols.tst

Page 99: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 99

Table Functions

A table function is a function that returns a collection (nested table or varray) that can be queried like a relational table or manipulated in PL/SQL as a collection variable.

A special form of the table function (PIPELINED) can be executed in parallel, offering significant performance improvements.– Especially useful with data warehouse applications.

SELECT c.name, Book.name, Book.author, Book.abstract FROM Catalogs c, TABLE (rankings_pkg.most_popular (c.cat)) Book;

Page 100: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 100

The importance of table functions

Table functions allow you to perform arbitrarily complex transformations of data and then make that data available through a query.– Not everything can be done in SQL.

Combined with REF CURSORs, you can now more easily transfer data from within PL/SQL to host environments.– Java, for example, works very smoothly with cursor

variables

Page 101: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 101

Building a table function

Requirements for a table function:– Return a nested table or varray based on a

schema-defined type, or type defined in a PL/SQL package.

– The function header and the way it is called must be SQL-compatible: all parameters use SQL types; no named notation.

Let's go through a construction exercise.– My company breeds different kinds of animals for

sale as pets.pet_family.sql

Page 102: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 102

Define the nested table and associated type

You must first define the datatypes you will be referencing in your table function.– If you want to return more than a single scalar

value, you will need to define a schema-level object type.

– Note: you cannot use <table>%ROWTYPE.

CREATE TYPE pet_t IS OBJECT ( NAME VARCHAR2 (60) , breed VARCHAR2 (100) , dob DATE);

CREATE TYPE pet_nt IS TABLE OF pet_t;pet_family.sql

Page 103: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 103

Write the function

Obviously, this is very much application-specific.

The point to keep in mind is that your logic can get just as wild and crazy as necessary to create the data you need.

CREATE OR REPLACE FUNCTION pet_family ( dad_in IN pet_t, mom_in IN pet_t) RETURN pet_ntIS l_count PLS_INTEGER; retval pet_nt := pet_nt ();BEGIN retval.EXTEND (2); retval (retval.LAST) := dad_in; retval (retval.LAST) := mom_in;

IF mom_in.breed = 'RABBIT' THEN l_count := 12; ELSIF mom_in.breed = 'DOG' THEN l_count := 4; ELSIF mom_in.breed = 'KANGAROO' THEN l_count := 1; END IF;

FOR indx IN 1 .. l_count LOOP retval.EXTEND; retval (retval.LAST) := pet_t ('BABY' || indx , mom_in.breed, SYSDATE - indx); END LOOP;

RETURN retval;END pet_family; pet_family.sql

Page 104: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 104

Call the function inside an SQL query

I pass in two objects, dynamically created with a call to the object constructor function.

I enclose the function call in the TABLE operator. The column names correspond to the names of the

columns in the object type.– If a table of scalars, then COLUMN_VALUE is the column

name.

SELECT pets.NAME, pets.dob FROM TABLE (pet_family (pet_t ('Hoppy', 'RABBIT', SYSDATE-450) , pet_t ('Hippy', 'RABBIT', SYSDATE-300) ) ) pets;

pet_family.sqltabfunc_scalar.sql

Page 105: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 105

Return a cursor variable from the function

CREATE OR REPLACE FUNCTION pet_family_cv RETURN sys_refcursorIS retval sys_refcursor;BEGIN OPEN retval FOR SELECT * FROM TABLE (pet_family (pet_t ('Hoppy', 'RABBIT', SYSDATE) , pet_t ('Hippy', 'RABBIT', SYSDATE) ) );

RETURN retval;END pet_family_cv;

pet_family.sql

DECLARE TYPE pet_r IS RECORD ( NAME VARCHAR2 (60), breed VARCHAR2 (100), dob DATE);

rec pet_r; cv sys_refcursor;BEGIN cv := pet_family_cv;

LOOP FETCH cv INTO rec; EXIT WHEN cv%NOTFOUND; DBMS_OUTPUT.put_line (rec.NAME); END LOOP;END;

Page 106: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 106

Stream data with table functions

Data streaming means that you can pass data from one process or stage to another without intermediate staging.– No need to instantiate and fill a local data structure.– Can improve performance.

Let's look at a typical data warehouse example: pivoting of data.

BEGIN INSERT INTO tickertable SELECT * FROM TABLE (stockpivot (CURSOR (SELECT * FROM stocktable)));END;

tabfunc_streaming.sql

Page 107: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 107

About pipelined table functions

Pipelined functions allow you to return data iteratively, asynchronous to termination of the function.– As data is produced within the function, it is passed back to

the calling process/query.

Pipelined functions can be defined to support parallel execution.– Iterative data processing allows multiple processes to work on

that data simultaneously.

CREATE FUNCTION StockPivot (p refcur_pkg.refcur_t) RETURN TickerTypeSet PIPELINED

Page 108: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 108

Piping rows out from a pipelined function

CREATE FUNCTION stockpivot (p refcur_pkg.refcur_t) RETURN tickertypeset PIPELINED IS out_rec tickertype := tickertype (NULL, NULL, NULL); in_rec p%ROWTYPE;BEGIN LOOP FETCH 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;

tabfunc_setup.sqltabfunc_pipelined.sql

Add PIPELINED keyword to header

Pipe a row of data back to calling block or query

RETURN...nothing at all!

Page 109: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 109

Parallel Execution and Table Functions

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.

Page 110: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 110

Enabling Parallel Execution

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).

{[ORDER | CLUSTER] BY column_list} PARALLEL_ENABLE ({PARTITION p BY [ANY | (HASH | RANGE) column_list]} )

Page 111: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 111

Examples of Parallelized Functions

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.

Page 112: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 112

Table functions - Summary

Table functions offer significant new flexibility for PL/SQL developers.

Consider using them when you...– Need to pass back complex result sets of data

through the SQL layer (a query);– Want to call a user defined function inside a

query and execute it as part of a parallel query.

Page 113: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 113

Built-in package enhancements of note

UTL_FILE– Not nearly as limited as before

DBMS_UTILITY.FORMAT_ERROR_BACKTRACE

DBMS_SCHEDULER UTL_RECOMP UTL_MAIL DBMS_OUTPUT.PUT_LINE

– Big strings! Big buffer! (Oracle10g Release 2)

Page 114: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 114

New and Improved UTL_FILE

With UTL_FILE, you can now:– UTL_FILE.FREMOVE - Remove a file– UTL_FILE.FRENAME - Rename a file, and also in effect

move files– UTL_FILE.FCOPY - Copy all or part of one file to another– UTL_FILE.FGETATTR - Retrieves attributes of the file,

such as its length

You can also use a database DIRECTORY to specify the location of the file; UTL_FILE_DIR will be ignored!!!!

Oracle9i Release 2

Page 115: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 115

Working with Database Directories

A Directory is a database "object", define with a CREATE statement.– You need CREATE ANY DIRECTORY privilege.

utlfile_92.sql

CREATE OR REPLACE DIRECTORY ERROR_LOG AS '/tmp/apps/log';

SELECT owner, directory_name, directory_path FROM ALL_DIRECTORIES;

View those directories to which you have

access.

GRANT READ ON DIRECTORY error_log TO SCOTT;Grant READ or WRITE privileges on a

directory.

Page 116: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 116

Copy a File

You can specify an OS directory or a database object of type DIRECTORY (as shown above)

DECLARE file_suffix VARCHAR2 (100) := TO_CHAR (SYSDATE, 'YYYYMMDDHHMISS');BEGIN -- Copy the entire file... UTL_FILE.fcopy ( src_location => 'DEVELOPMENT_DIR', src_filename => 'archive.zip', dest_location => 'ARCHIVE_DIR', dest_filename => 'archive' || file_suffix || '.zip' );END;

fcopy.sqlfileIO92.pkg

Page 117: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 117

Remove a File

If no error is raised, then you deleted successfully

BEGIN UTL_FILE.fremove ( src_location => 'DEVELOPMENT_DIR', src_filename => 'archive.zip' );EXCEPTION -- If you call FREMOVE, you should check explicitly -- for deletion failures. WHEN UTL_FILE.delete_failed THEN ... Deal with failure to removeEND;

fremove.sqlfileIO92.pkg

Page 118: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 118

Rename/move a File

You specify target location and file name

DECLARE file_suffix VARCHAR2 (100) := TO_CHAR (SYSDATE, 'YYYYMMDD');BEGIN -- Rename/move the entire file in a single step. UTL_FILE.frename ( src_location => 'DEVELOPMENT_DIR', src_filename => 'archive.zip', dest_location => 'ARCHIVE_DIR', dest_filename => 'archive' || file_suffix || '.zip', overwrite => FALSE );EXCEPTION WHEN UTL_FILE.rename_failed THEN ... Deal with failure to renameEND;

frename.sqlfileIO92.pkg

Page 119: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 119

Obtaining attributes of a file

CREATE OR REPLACE FUNCTION flength ( location_in IN VARCHAR2, file_in IN VARCHAR2) RETURN PLS_INTEGERIS TYPE fgetattr_t IS RECORD ( fexists BOOLEAN, file_length PLS_INTEGER, block_size PLS_INTEGER );  fgetattr_rec fgetattr_t;BEGIN UTL_FILE.fgetattr ( location => location_in, filename => file_in, fexists => fgetattr_rec.fexists, file_length => fgetattr_rec.file_length, block_size => fgetattr_rec.block_size ); RETURN fgetattr_rec.file_length;END flength;

How big is a file? What is its block size? Does the file exist?

All valuable questions. All answered with a call

to UTL_FILE.FGETATTR.

flength.sqlfileIO92.pkg

Page 120: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 120

DBMS_UTILITY.FORMAT_ERROR_BACKTRACE

Before Oracle10g, the only way is to let the error go unhandled in your PL/SQL code!

DBMS_UTILITY.FORMAT_ERROR_STACK only gives you the full error message.– And is recommended by Oracle in place of SQLERRM.

Long-standing challenge in PL/SQL:

How can I find the line number on which an error was raised in PL/SQL?

But in Oracle10g, we have "back trace"!

Page 121: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 121

Letting the error go unhandled…

CREATE OR REPLACE PROCEDURE proc1 ISBEGIN DBMS_OUTPUT.put_line ('running proc1'); RAISE NO_DATA_FOUND;END;/CREATE OR REPLACE PROCEDURE proc2 IS l_str VARCHAR2(30) := 'calling proc1';BEGIN DBMS_OUTPUT.put_line (l_str); proc1; END;/CREATE OR REPLACE PROCEDURE proc3 ISBEGIN DBMS_OUTPUT.put_line ('calling proc2'); proc2;END;/

ERROR at line 1:ORA-01403: no data foundORA-06512: at "SCOTT.PROC1", line 7ORA-06512: at "SCOTT.PROC2", line 8ORA-06512: at "SCOTT.PROC3", line 5ORA-06512: at line 3

Backtrace.sql

Page 122: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 122

Displaying the “error stack” inside PL/SQL

CREATE OR REPLACE PROCEDURE proc3ISBEGIN DBMS_OUTPUT.put_line ('calling proc2'); proc2;EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.put_line ( DBMS_UTILITY.FORMAT_ERROR_STACK);END;/

SQL> exec proc3calling proc2calling proc1running proc1ORA-01403: no data foundbacktrace.sql

Page 123: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 123

Displaying the contents of BACKTRACE

CREATE OR REPLACE PROCEDURE proc3ISBEGIN DBMS_OUTPUT.put_line ('calling proc2'); proc2;EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.put_line ('Error stack at top level:'); DBMS_OUTPUT.put_line (DBMS_UTILITY.FORMAT_ERROR_BACKTRACE);END;/

SQL> exec proc3calling proc2calling proc1running proc1Error stack at top level:ORA-06512: at "SCOTT.PROC1", line 5ORA-06512: at "SCOTT.PROC2", line 7ORA-06512: at "SCOTT.PROC3", line 5

backtrace.sqlbt.pkg

Page 124: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 124

The BACKTRACE stack with re-RAISEs

CREATE OR REPLACE PROCEDURE proc1 ISBEGIN …EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.put_line ( 'Error stack in block where raised:'); DBMS_OUTPUT.put_line ( DBMS_UTILITY.format_error_backtrace); RAISE;END;/CREATE OR REPLACE PROCEDURE proc3 ISBEGIN DBMS_OUTPUT.put_line ('calling proc2'); proc2;EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.put_line ('Error stack at top level:'); DBMS_OUTPUT.put_line (DBMS_UTILITY.format_error_backtrace); bt.show (DBMS_UTILITY.format_error_backtrace);END;/

SQL> exec proc3calling proc2calling proc1running proc1Error stack in block where raised:ORA-06512: at "SCOTT.PROC1", line 5

Error stack at top level:ORA-06512: at "SCOTT.PROC1", line 11ORA-06512: at "SCOTT.PROC2", line 7ORA-06512: at "SCOTT.PROC3", line 5

Program owner = SCOTTProgram name = PROC1Line number = 11

Page 125: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 125

The BACKTRACE stack with new RAISE

CREATE OR REPLACE PROCEDURE proc1ISBEGIN DBMS_OUTPUT.put_line ('running proc1'); RAISE NO_DATA_FOUND;EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.put_line ('Error stack in block where raised:'); DBMS_OUTPUT.put_line (DBMS_UTILITY.format_error_backtrace); RAISE;END;/

CREATE OR REPLACE PROCEDURE proc2ISBEGIN DBMS_OUTPUT.put_line ('calling proc1'); proc1;EXCEPTION WHEN OTHERS THEN RAISE VALUE_ERROR;END;/

SQL> exec proc3calling proc2calling proc1running proc1Error stack in block where raised:ORA-06512: at "SCOTT.PROC1", line 5

Error stack at top level:ORA-06512: at "SCOTT.PROC2", line 9ORA-06512: at "SCOTT.PROC3", line 5

Program owner = SCOTTProgram name = PROC2Line number = 9

Page 126: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 126

DBMS_OUTPUT: relief in sight!

Oracle10g Release 2 offers some long-awaited enhancements...– DBMS_OUTPUT.PUT_LINE will now

accept and display strings up to 32K in length.

– You can set the buffer size to UNLIMITED.

SET SEVEROUTPUT ON SIZE UNLIMITED

32K!

Page 127: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 127

DBMS_SCHEDULER

Oracle10g offers a new, built-in job scheduler to help you take care of routine tasks.– Much improved over DBMS_JOB.– Lets you control database resources and assign

priorities.

Work with the scheduler either through the Oracle10g Enterprise Manager Database Control or the DBMS_SCHEDULER package.

We will take a brief look at some of the features of the built-in package.

Oracle10g

Some graphics and content courtesy of Oracle Magazine.

Page 128: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 128

Job scheduler components

A job combines a program and schedule.– The program defines what is to be run. – For example, a program can be a PL/SQL block, a stored procedure, or

an operating system script. – The schedule defines when the program runs.

Jobs, schedules, and programs are the three core components you must master to begin using the scheduler.

Page 129: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 129

Job scheduler resource management

Job classes connect the scheduling system with the resource management system, giving you control over how database resources are allocated to running jobs.

Scheduler windows can control when different resource plans take effect. Window groups group together related windows.– Windows and window groups give you a great deal of control over how

you can allocate database resources to different job classes.

Page 130: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 130

Create a program

The program defines the stored procedure, PL/SQL block or operating system program that you want to execute on a schedule.

BEGIN dbms_scheduler.create_program (program_name => 'EMP_MAINT.LOAD_HISTORY' , program_action => 'BEGIN load_history_data; END;' , program_type => 'PLSQL_BLOCK' , number_of_arguments => 0 , comments => 'Load employee history' , enabled => TRUE );END;

Page 131: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 131

Create a schedule

Define the schedule. You can specify....– Start date, end date, frequency and duration.

– For a one-off job, a schedule will include only a start time. For repeating jobs, you can specify a start time; a repetition schedule; and, optionally, an end time.

BEGIN dbms_scheduler.create_schedule (repeat_interval => 'FREQ=DAILY;INTERVAL=2;BYHOUR=18;BYMINUTE=0;BYSECOND=0' , start_date => TO_TIMESTAMP_TZ ('2004-03-22 US/Eastern', 'YYYY-MM-DD TZR') , comments => 'Schedule for periodic loads of customer-related data' , schedule_name => '"EMP_MAINT"."HISTORY_LOADS"' );END;

Page 132: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 132

Create a window

A job scheduler window lets you specify...– Start date, end date, frequency and duration.– Database resources and priorities.

BEGIN dbms_scheduler.create_window (window_name => 'night' , resource_plan => 'BATCH_PROCESSING' , start_date =>

TO_TIMESTAMP_TZ ('2004-03-22 US/Eastern', 'YYYY-MM-DD TZR') , DURATION => NUMTODSINTERVAL (840, 'minute') , repeat_interval => 'FREQ=DAILY;BYHOUR=18;BYMINUTE=0;BYSECOND=0' , end_date => NULL , window_priority => 'LOW' , comments => '' );END;

Page 133: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 133

Use OEM Database Control

If you don't need to define your database jobs programmatically, you would be best off using the graphical interface available in OEM Database Control.

Page 134: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 134

UTL_RECOMP

The UTL_RECOMP built-in package offers two programs that you can use to recompile any invalid objects in your schema: RECOMP_SERIAL and RECOMP_PARALLEL. – Must connect as SYSDBA account to use

UTL_RECOMP.– Parallel version uses DBMS_JOB and will temporarily

disable all other jobs in the queue to avoid conflicts with the recompilation.

Oracle10g

CALL utl_recomp.recomp_serial ('SCOTT'); CALL utl_recomp.recomp_parallel ('SCOTT', 4); recompile.sql

Page 135: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 135

UTL_MAIL

UTL_MAIL makes it much easier to send email from within PL/SQL by hiding some of the complexities of UTL_SMTP.

To use UTL_MAIL...– Set the SMTP_OUTPUT_SERVER parameter.– Install the utlmail.sql and prvtmail.plb files under

SYS. That's right - it is not installed by default.– Grant EXECUTE on UTL_MAIL as desired.

Oracle10g

Page 136: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 136

Send an email message from PL/SQL

The interface to the SEND program mimics the basic "send email" form of Outlook and other email programs.

BEGIN /* Requires Oracle10g */ UTL_MAIL.send ( sender => '[email protected]' ,recipients => '[email protected], [email protected]' ,cc => '[email protected]' ,bcc => '[email protected]' ,subject => 'Cool new API for sending email' ,message =>'Hi Ya''ll,Sending email in PL/SQL is *much* easier with UTL_MAIL in 10g. Give it a try!Mailfully Yours,Bill' );END;

Page 137: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 137

Attachments and UTL_MAIL

You can attach RAW or VARCHAR2 content as an attachment (up to 32K).

BEGIN /* Requires Oracle10g */ UTL_MAIL.send_attachment_raw ( sender => '[email protected]' ,recipients => '[email protected], [email protected]' ,cc => '[email protected]' ,bcc => '[email protected]' ,subject => 'Cool new API for sending email' ,message => '...' ,attachment => '...' /* Content of the attachment */ ,att_inline => TRUE /* Attachment in-line? */ ,att_filename => '...' /* Name of file to hold the attachment after the mail is received. */ );END;

Page 138: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 138

Acknowledgements and Resources

Very few of my ideas are truly original. I have learned from every one of these books and authors – and you can, too!

Page 139: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 139

A guide to my mentors/resources

A Timeless Way of Building – a beautiful and deeply spiritual book on architecture that changed the way many developers approach writing software.

Peopleware – a classic text on the human element behind writing software. Refactoring – formalized techniques for improving the internals of one's code

without affect its behavior. Code Complete – another classic programming book covering many aspects of

code construction. The Cult of Information – thought-provoking analysis of some of the down-

sides of our information age. Patterns of Software – a book that wrestles with the realities and problems with

code reuse and design patterns. Extreme Programming Explained – excellent introduction to XP. Code and Other Laws of Cyberspace – a groundbreaking book that recasts

the role of software developers as law-writers, and questions the direction that software is today taking us.

Page 140: 21st Century PLSQL

Copyright 2000-2005 Steven Feuerstein - Page 140

So Much to Learn...

Don't panic -- but don't stick your head in the sand, either.– You won't thrive as an Oracle7, Oracle8 or Oracle8i

developer!

You can do so much more from within PL/SQL than you ever could before.– Familiarity with new features will greatly ease the

challenges you face.


Top Related