Sunteți pe pagina 1din 151

Oracle PL/SQL Programming

A Speak-Tech Training

High Performance PL/SQL


Plus New Features in Oracle Database 11g

Steven Feuerstein
steven@stevenfeuerstein.com www.StevenFeuerstein.com

Oracle PL/SQL Programming

High Performance PL/SQL - Agenda


Analyzing performance and memory
Runtime Memory Management and PL/SQL Profilers and timers

The optimizing PL/SQL compiler Data Caching Techniques


DETERMINISTIC , PGA caching, Function Result Cache

Other Collection Performance Techniques


String indexing, MULTISET operators

Bulk Processing with BULK COLLECT and FORALL Table Functions including Pipelined TFs NOCOPY Optimizing Datatypes
Page 2

Copyright 2011 Feuerstein and Associates

Oracle PL/SQL Programming

High Performance PL/SQL Agenda


Suppressing Errors with LOG ERRORS Optimizing SQL in PL/SQL
The RETURNING Clause Most efficient way to fetch a single row Tips for dynamic SQL Updating large tables in parallel with DBMS_PARALLEL_EXECUTE

Other Oracle Database 11g features


Dynamic SQL enhancements Other non-performance related features
Copyright 2011 Feuerstein and Associates Page 3

Oracle PL/SQL Programming

How to benefit most from this training


Watch, listen, ask questions, focus on concepts and principles. Download and use any of my training materials:

PL/SQL Obsession

http://www.ToadWorld.com/SF

Download and use any of my scripts (examples, performance scripts, reusable code) from the same location: the demo.zip file.
filename_from_demo_zip.sql

You have my permission to use all these materials to do internal trainings and build your own applications.
But remember: they are not production ready. You must test them and modify them to fit your needs.
Copyright 2011 Feuerstein and Associates Page 4

Oracle PL/SQL Programming

Websites for PL/SQL Developers


www.plsqlchallenge.com
Daily PL/SQL quiz with weekly and monthly prizes

www.plsqlchannel.com
27+ hours of detailed video training on Oracle PL/SQL

www.stevenfeuerstein.com
Monthly PL/SQL newsletter

www.toadworld.com/SF
Quest Software-sponsored portal for PL/SQL developers

Copyright 2011 Feuerstein and Associates

Page 5

Oracle PL/SQL Programming

Other Key Sites for PL/SQL Developers


oracle-developer.net
Adrian Billington's fantastic site

Oracle Performance Survival Guide


Book and scripts by Guy Harrision guyharrison.squarespace.com/opsgsamples

Oracle-BASE.com
Tim Hall's incredibly useful set of resources and scripts

Ask Tom - Tom Kyte's famed forum


Copyright 2011 Feuerstein and Associates Page 6

Oracle PL/SQL Programming

What I won't be talking about


Tuning SQL statements
I am not an expert in explain plans, hints, the CBO, etc.

Configuring the database


The DBA must make sure that, among other things, all of the many caches in the System Global Area are big enough to avoid swapping due to application of the Least Recently Used (LRU) algorithm.

Instead, I will focus on changes you can make in the way you write PL/SQL that will impact performance.
Copyright 2011 Feuerstein and Associates Page 7

Oracle PL/SQL Programming

When to optimize your code


We always want our programs to run faster. But remember the 80-20 rule:
Most of your code will never be part of a bottleneck, so don't obsess about optimizing every line of code.

Make sure that you are familiar with the most critical optimization features.
Apply these proactively.

Then prioritize maintainability of code. Afterwards, apply more specialized techniques.


Copyright 2011 Feuerstein and Associates Page 8

Oracle PL/SQL Programming

To Optimize, You Need To...


Profile execution of code
Identify performance bottlenecks

Calculate elapsed time of execution


Critical for granular analysis of performance and comparison of performance between different implementations of same program

Manage memory
Most optimizations involve a tradeoff: less CPU, more memory.
Copyright 2011 Feuerstein and Associates Page 9

Oracle PL/SQL Programming

Profiling execution of PL/SQL code


First of all, is it SQL or is it PL/SQL?
Most of the time, SQL is the problem. Use the v$SQL view to answer the question.

Profile the execution of your PL/SQL program units to identify bottlenecks.


Which lines consume the most CPU? Which subprograms take the most time?

Two profilers:
DBMS_PROFILER: line by line performance DBMS_HPROF: hierarchical profiler, rollup to program units
Copyright 2011 Feuerstein and Associates

cachedPLSQL.sql

Page 10

Oracle PL/SQL Programming

DBMS_PROFILER
BEGIN DBMS_OUTPUT.PUT_LINE ( DBMS_PROFILER.START_PROFILER ( 'my_application ' || TO_CHAR (SYSDATE, 'YYYYMDD HH24:MI:SS') )); run_your_application; DBMS_PROFILER.STOP_PROFILER; END;

Requires EXECUTE privilege on DBMS_PROFILER. Must create tables: $RDBMS_ADMIN/proftab.sql Run queries (or use your IDE) to view results. This profiler also provides raw data for code coverage analysis.
profrep.sql dbms_profiler_example.sql
Page 11

Copyright 2011 Feuerstein and Associates

11g

Oracle PL/SQL Programming

DBMS_HPROF
DECLARE l_runid BEGIN NUMBER;

DBMS_HPROF.start_profiling ('HPROF_DIR', 'run1');


run_application;

DBMS_HPROF.stop_profiling (); l_runid := DBMS_HPROF.analyze (location


, , END; $ plshprof -output hprof run1.trc filename run_comment

=> 'HPROF_DIR'

=> 'run1.trc' => 'First run');

Requires EXECUTE privilege on DBMS_PROFILER. Must create tables: $RDBMS_ADMIN/dbmshptab.sql Run queries against tables or call plshprof to generate HTML reports
Copyright 2011 Feuerstein and Associates

dbms_hprof_example.sql

Page 12

Oracle PL/SQL Programming

Calculating Elapsed Time of Programs


Many options for analyzing Oracle performance: TKPROF, SET TIMING ON, etc.
But they usually don't offer the granularity I need for my PL/SQL performance analysis.

Oracle offers DBMS_UTILITY.GET_TIME and GET_CPU_TIME (10g) to compute elapsed time down to the hundredth of a second.
Can also use SYSTIMESTAMP
DECLARE l_start_time PLS_INTEGER; BEGIN l_start_time := DBMS_UTILITY.get_time; -- Do stuff here... DBMS_OUTPUT.put_line ( DBMS_UTILITY.get_time l_start_time); END;
Copyright 2011 Feuerstein and Associates

sf_timer.* get_time.sql plvtmr.* plvtmr_ts.pkg tmr.ot thisuser*.*

Page 13

Oracle PL/SQL Programming

Whats the Problem?


What will happen when I run this code?
DECLARE l_strings DBMS_SQL.varchar2a; BEGIN FOR indx IN 1 .. 2 ** 31 - 1 LOOP l_strings (indx) := RPAD ('abc', 32767, 'def'); END LOOP; END; /

Copyright 2011 Feuerstein and Associates

memory_error.sql

Page 14

Oracle PL/SQL Programming

Analyze Memory Usage of PL/SQL Code


It is certainly possible to write PL/SQL code that consumes so much memory, it kills a user's session.
It's quite easy to do, in fact.

As you work with more advanced features, like collections and FORALL, you will need to pay attention to memory, and make adjustments. First, let's review how Oracle manages memory at run-time.
Copyright 2011 Feuerstein and Associates Page 15

Oracle PL/SQL Programming

PL/SQL Runtime Memory Architecture


System Global Area (SGA) of RDBMS Instance
Shared Pool
Shared SQL Reserved Pool Pre-parsed
Select * from emp

Library cache
Update emp Set sal=...

Large Pool

calc_totals

show_emps

upd_salaries

Session 1

emp_rec emp%rowtype; tot_tab pkg.tottabtype;

emp_rec emp%rowtype; tot_tab pkg.tottabtype;

Session 2

Session 1 memory UGA User Global Area PGA Process Global Area
Copyright 2011 Feuerstein and Associates

Session 2 memory UGA User Global Area PGA Process Global Area
Page 16

Oracle PL/SQL Programming

How PL/SQL uses the SGA, PGA and UGA


The SGA contains information that can be shared across schemas connected to the instance.
From the PL/SQL perspective, this is limited to package static constants.
PACKAGE Pkg is Nonstatic_Constant CONSTANT PLS_INTEGER := My_Sequence.Nextval; Static_Constant CONSTANT PLS_INTEGER := 42; END Pkg;

The Process Global Area contains session-specific data that is released when the current server call terminates.
Local data

The User Global Area contains session-specific data that persists across server call boundaries
Package-level data
Copyright 2011 Feuerstein and Associates

top_pga.sql

Page 17

Oracle PL/SQL Programming

Calculating PGA and UGA Consumption


SELECT n.name, s.VALUE FROM sys.v_$sesstat s, sys.v_$statname n WHERE s.statistic# = n.statistic# AND s.sid = my_session.sid AND n.name IN ('session uga memory', 'session pga memory')

Oracle keeps track of and shows the PGA and UGA consumption for a session in the v_$sesstat dynamic view. With the correct privileges, PL/SQL developers can analysis their code's memory usage.
BEGIN plsql_memory.start_analysis; run_my_application; plsql_memory.show_memory_usage; END;
Copyright 2011 Feuerstein and Associates

show_pga_uga.sql grantv$.sql plsql_memory.pkg plsql_memory_demo.sql

Page 18

Oracle PL/SQL Programming

Tips for managing memory


Use LIMIT clause with BULK COLLECT. Use varrays with BULK COLLECT to declaratively guard against "memory creep." Use NOCOPY hint when passing IN OUT collections. Be very careful about defining variables at the package level.
Memory will not be released when the block terminates.

Use pipelined table functions.


Copyright 2011 Feuerstein and Associates

bulklimit.sql varray_collection_limit.sql nocopy*.tst tabfunc_pipelined.sql


Page 19

Oracle PL/SQL Programming

Conclusions - Memory Management


Oracle takes responsibility for managing memory used for data (user data and "metadata" program code, table definitions, etc.) shared by multiple connections.
Based on parameter set by DBAs.

It is up to developers and DBAs to determine how much PGA memory can be used per connection. Then developers must make the necessary changes in their code to conform to that limit.

Copyright 2011 Feuerstein and Associates

Page 20

Oracle PL/SQL Programming

Whats the Problem?


What can I change in this program to improve performance?
CREATE OR REPLACE PROCEDURE plch_loop ( value1 IN NUMBER, value2 IN NUMBER, value3 IN NUMBER) IS l_result NUMBER := 0; BEGIN FOR indx IN 1 .. 10000000 LOOP l_result := l_result + (value1 + value2) / value3; END LOOP; DBMS_OUTPUT.put_line (l_result); END; /

Copyright 2011 Feuerstein and Associates

loop_invariants*.sql

Page 21

Oracle PL/SQL Programming

Fully Leverage the PL/SQL Compiler


Oracle demonstrated its long-term commitment to PL/SQL with the release of Oracle Database 10g
Many new features and a complete re-write of the compiler.

Automatic, transparent optimization of code Compile-time warnings framework to help you improve the quality of your code. Conditional compilation: you decide what code should be compiled/ignored!
Copyright 2011 Feuerstein and Associates Page 22

Oracle PL/SQL Programming

The Optimizing Compiler


The PL/SQL compiler now has the ability to automatically optimize your code.
The compiler rearranges your code. Compile time increases, runtime performance improves.

You choose the level of optimization :


0 Pre-10g compilation without optimization 1 Smaller scale change, less impact on compile times 2 Most aggressive, maximum possible code transformations, biggest impact on compile time. [default] 3 (Oracle11g) In-lining of local subprograms, in addition to all the optimization performed at level 2

Stick with the default, unless you have a clear need for an exception.
Copyright 2011 Feuerstein and Associates Page 23

Oracle PL/SQL Programming

The PL/SQL Optimizer: High Level View


The optimizer takes advantage of "freedoms" to reorder the execution of statements.
In essence, changing the route that the runtime engine takes to get from point A to point B in your code.

Some examples:
Unless otherwise specified, the operands of an expression operator may be evaluated in any order. Operands of a commutative operator may be commuted. The actual arguments of a call or a SQL statement may be evaluated in any order (including default actual arguments).

Optimization does not change the logical behavior of your code.


Optimization should not, for example, cause any of your regression tests to suddenly fail!
Copyright 2011 Feuerstein and Associates Page 24

Oracle PL/SQL Programming

Some Examples
... A + B ... ... ... A + B ...

T := A + B; ... T ... ... ... T ...

T is a generated variable. We never see it. And one operation is saved.

for i in 1 .. 10 loop A := B + C; ... end loop; A := B + C; for i in 1 .. 10 loop ... end loop; FOR rec in (SELECT ...) LOOP ... do stuff END LOOP;

Automatic relocation of a loop invariant. Avoid repetitive computations.

SELECT ... BULK COLLECT INTO ... FROM ...

Execute cursor FOR loop at BULK COLLECT levels of performance.

Copyright 2011 Feuerstein and Associates

10g_optimize_cfl.sql

Page 25

Oracle PL/SQL Programming

Things to Keep in Mind


my_function () * NULL

The PL/SQL runtime engine will always execute your subprograms, even if the optimizer detects that the results of that subprogram call are "not needed."
Exception: DETERMINISTIC functions in 11g

You cannot rely on a specific order of evaluation of arguments in a subprogram call or even when package initialization takes place.
The compiler will even avoid initialization of a package if it not needed (using a TYPE for example).
Copyright 2011 Feuerstein and Associates Page 26

Oracle PL/SQL Programming

Changing the optimizer level


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:
ALTER PROCEDURE bigproc COMPILE PLSQL_OPTIMIZE_LEVEL = 1;

and then:
ALTER PROCEDURE bigproc COMPILE REUSE SETTINGS;

Copyright 2011 Feuerstein and Associates

Page 27

11g

Oracle PL/SQL Programming

Oracle11g In-lining optimization


A new level, 3, tells Oracle to automatically search out opportunities to "inline" code for nested subprograms.
This means that a pointer to the subprogram is replaced with the implementation of the subprogram.

Oracle's own tests have shown 10-20% performance improvement.


Depends on how many local modules you create and how often they are used.

Note: compile code size increases.


ALTER SESSION SET PLSQL_OPTIMIZE_LEVEL = 3;
Copyright 2011 Feuerstein and Associates Page 28

11g

Oracle PL/SQL Programming

Selective Inlining with PRAGMA


PRAGMA INLINE (subprogram, 'YES')

You can also keep the optimization level at 2 and request inlining explicitly for specific subprogram invocations with a new INLINE pragma. Inlining applies to the following statements:
Assignment, CALL, conditional, CASE, CONTINUE-WHEN, EXECUTE IMMEDIATE, EXIT-WHEN, LOOP, RETURN

You can also request inlining for all executions of the subprogram by placing the PRAGMA before the declaration of the subprogram. Inlining, like NOCOPY, is a request.
Under some circumstances, inlining will not take place.
Copyright 2011 Feuerstein and Associates

11g_inline*.sql

Page 29

11g

Oracle PL/SQL Programming

Inlining Could Slow Down Code


Oracle warns that inlining occurs early in the optimization process and may "preclude later, more powerful optimizations." If you find that inlining is slowing down a program unit, profile execution to identify subprograms for which to turn off inlining.
Oracle recommends the new-to-11g hierarchical profiler, DBMS_HPROF.

Selectively disable inlining with pragma:


PRAGMA INLINE (subprogram, 'NO')

Copyright 2011 Feuerstein and Associates

Page 30

Oracle PL/SQL Programming

Learn more about the PL/SQL optimizer


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

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.

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.

PL/SQL Performance Measurement Harness


Describes a performance experiment whose conclusion is the large factors quoted above. Oracle provides a downloadable kit to enable you to repeat the experiment yourself.
Copyright 2011 Feuerstein and Associates Page 31

Oracle PL/SQL Programming

Data Caching Options in PL/SQL


A time-honored technique for improving performance. Store data that doesnt change for some period of time in a location that can be accessed more quickly than the source. The SGA is an enormous, complex cache for the entire database instance. But there are other caches (in SGA and PGA) we can leverage in our PL/SQL code.
Copyright 2011 Feuerstein and Associates Page 32

Oracle PL/SQL Programming

Data Caching Options


Functions declared as DETERMINISTIC PGA caching
Used most effectively with collections Accessing PGA memory generally more efficient than SGA, especially if executing SQL.

Oracle Database 11g Function Result Cache


The best caching technique and the most important new feature in 11g for PL/SQL developers.
Copyright 2011 Feuerstein and Associates Page 33

Oracle PL/SQL Programming

DETERMINSTIC Functions
A function is deterministic if the value it returns is determined completely by its inputs (IN arguments).
In other words, no side effects.

Add the DETERMINISTIC keyword to your function to enable optimizations:


Function-based indexes Cache results in scope of a query

Dont lie! Oracle will not reject your use of the keyword, even if it isnt true.
Copyright 2011 Feuerstein and Associates

deterministic.sql deterministic_in_plsql.sql

Page 34

Oracle PL/SQL Programming

PGA-Based Caching
When you declare variables at the package level, their state persists in your session.
A PGA-based cache, specific to each session.

And if you declare a collection at the package level, you can cache multiple rows of data. Not a reliable technique for Web-based (usually stateless) applications Let's start with a trivial example: USER
thisuser*.*

Copyright 2011 Feuerstein and Associates

Page 35

Oracle PL/SQL Programming

PGA Caching with Collections


Collections are PL/SQL's "version" of arrays. You can define and manipulate collections of scalars (numbers, strings, etc.) or much more complex data structures (records, nested collections). Which means you can cache multiple rows of complex data. In this course, I will offer a quick introduction to collections.
If they are new for you, time to study!
Copyright 2011 Feuerstein and Associates

associative_array_example.sql nested_table_example.sql

Page 36

Oracle PL/SQL Programming

Avoiding Unnecessary SGA Lookups


First access
Database / SGA
Not in cache; Request data from database Pass Data to Cache

Data retrieved from cache

Data returned to application

Function

Application

PGA
Application Requests Data

Subsequent accesses
Database / SGA
Data found in cache. Database is not needed.

Data retrieved from cache

Data returned to application

Function

Application

PGA
Application Requests Data Copyright 2011 Feuerstein and Associates

emplu.pkg / emplu.tst

Page 37

Oracle PL/SQL Programming

PGA Caching: Things to keep in mind


Must use package-level data so that it persists.
Memory is consumed by the PGA and so is multiplied for all users of the application. Not a reliable technique for stateless application (Internet)

Very difficult to share cache across sessions in the same instance.


One possibility involves DBMS_PIPE.

Very difficult to update the cache once the data source is changed.
Especially by/from, other sessions. Possible to use DBMS_ALERT.

Useful under specific scenarios....


Small, static dataset Single or small number of batch processes
Copyright 2011 Feuerstein and Associates

syscache.pkg

Page 38

11g

Oracle PL/SQL Programming

The Oracle 11g Function Result Cache


Oracle offers a far superior caching solution than PGA caching in 11g: the Function Result Cache. This cache is...
stored in the SGA shared across sessions purged of dirty data automatically

You can use and should use it to retrieve data from any table that is queried more frequently than updated.
Copyright 2011 Feuerstein and Associates Page 39

11g

Oracle PL/SQL Programming

How the Function Result Cache Works


Add the RESULT_CACHE clause to your function's header. When a call is made to function, Oracle compares IN argument values to the cache. If no match, the function is executed and the inputs and return data are cached. If a match is found, the function is not executed; cached data is returned. If changes to a "relies on" table are committed, the cache is marked invalid and will be re-built.
Copyright 2011 Feuerstein and Associates

11g_frc_demo.sql

Page 40

11g

Oracle PL/SQL Programming

Minimal Impact on Code with Result Cache


CREATE OR REPLACE PACKAGE emplu11g IS FUNCTION onerow (employee_id_in IN employees.employee_id%TYPE) RETURN employees%ROWTYPE RESULT_CACHE; END emplu11g; CREATE OR REPLACE PACKAGE BODY emplu11g IS FUNCTION onerow (employee_id_in IN employees.employee_id%TYPE) RETURN employees%ROWTYPE RESULT_CACHE RELIES_ON (employees) IS .... END onerow; END emplu11g;

Add RESULT_CACHE keyword to header of function in both specification and body. RELIES_ON clause is deprecated in 11.2. Oracle will automatically determine all tables on which the function relies. RELIES_ON is then ignored.
Copyright 2011 Feuerstein and Associates Page 41

11g

Oracle PL/SQL Programming

Performance Impact of Result Cache


The result cache is stored in the SGA. So we should expect it be slower than a PGAbased cache. But accessing result cache data does not require going through the SQL engine. So it should be much faster than executing a query.
Even if the statement is parsed and the data blocks are already in the SGA.

Let's find out!


Copyright 2011 Feuerstein and Associates

11g_emplu*.*

Page 42

11g

Oracle PL/SQL Programming

Result Cache Things to Keep in Mind - 1


If you have uncommitted changes in your session, dependent caches are ignored.
The cache will not override your own changed data.

Caching is not performed for complex types: records with CLOBs, collections, etc. The cache is not related to SQL statements in your function.
It only keeps track of the input values and the RETURN clause data.
Copyright 2011 Feuerstein and Associates

11g_frc_demo.sql

Page 43

11g

Oracle PL/SQL Programming

Result Cache Things to Keep in Mind - 2


You cannot use the result cache with invoker rights program units.
Bypass execution of function body, Oracle cannot resolve references to objects - the whole point of IR.

Functions with session-specific dependencies must be "result-cached" with great care.


Virtual private database configurations References to SYSDATE, reliance on NLS_DATE_FORMAT, time zone changes Application contexts (calls to SYS_CONTEXT)

Solution: move all dependencies into parameter list.


Copyright 2011 Feuerstein and Associates

11g_frc_vpd.sql 11g_frc_vpd2.sql

Page 44

11g

Oracle PL/SQL Programming

Managing the Result Cache


Oracle offers a number of ways to manage the result cache and tune it to your specific application needs: RESULT_CACHE_SIZE initialization parameter
If the cache is too small, then the LRU algorithm negates the point of the cache.

DBMS_RESULT_CACHE management package v$RESULT_CACHE_* performance views

Copyright 2011 Feuerstein and Associates

show_frc_dependencies.sp

Page 45

11g

Oracle PL/SQL Programming

Fine Grained Dependencies in 11.2


Oracle keeps track of table dependencies on a per-result level.
Each result cached could have a different set of dependencies.

A change to a table could invalidate just a subset of the results in the cache.
It's not all or nothing - when your function's different logic paths could "hit" different tables.

Copyright 2011 Feuerstein and Associates

11g_frc_dependencies.sql 11g_frc_dependencies2.sql

Page 46

Oracle PL/SQL Programming

Conclusions - Caching
Oracle offers several different ways you can build upon its own caching. DETERMINISTIC for functions in SQL PGA caching is very fast, but cannot be used in most situations The function result cache is the simplest, most widely applicable, and biggest-impact technique.
Get ready for it now by hiding queries inside functions.
Copyright 2011 Feuerstein and Associates

11g_emplu.pkg

Page 47

Oracle PL/SQL Programming

Whats the Problem?


Here's a function that tells me is a particular string has been used.
CREATE OR REPLACE PACKAGE BODY string_tracker IS FUNCTION string_in_use (value_in IN variable_name_t) RETURN BOOLEAN IS c_count CONSTANT PLS_INTEGER := g_names_used.COUNT; l_index PLS_INTEGER := g_names_used.FIRST; l_found BOOLEAN DEFAULT FALSE; BEGIN WHILE (NOT l_found AND l_index <= c_count) LOOP l_found := value_in = g_names_used (l_index); l_index := l_index + 1; END LOOP; RETURN l_found; END string_in_use; END string_tracker;

Copyright 2011 Feuerstein and Associates

string_tracker0.pkg

Page 48

Oracle PL/SQL Programming

Other Performance-Related Collection Techniques


Using String Indexes with Associative Arrays Using MULTISET Operators with Nested Tables

Copyright 2011 Feuerstein and Associates

Page 49

Oracle PL/SQL Programming

Expanded indexing capabilities for associative arrays


Prior to Oracle9i Release 2, 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...)
Copyright 2011 Feuerstein and Associates Page 50

Oracle PL/SQL Programming

Working with string-indexed collections


The syntax for using string indexing is the same.
And all the same methods are available.

But the type of data returned by FIRST, LAST, NEXT and PRIOR methods is VARCHAR2.
The longer the string values, the more time it takes Oracle to "hash" or convert that string to the integer that is actually used as the index value.
Relatively small strings, say under 100 characters, do not incur too large a penalty.
assoc_array*.sql assoc_array_perf.tst

Copyright 2011 Feuerstein and Associates

Page 51

Oracle PL/SQL Programming

Tracking Usage with String Indexing


Rather than add each string to a list of used strings, why not use the string as the index?
CREATE OR REPLACE PACKAGE BODY string_tracker IS TYPE used_aat IS TABLE OF BOOLEAN INDEX BY VARCHAR2(32767); g_names_used used_aat; FUNCTION string_in_use ( value_in IN VARCHAR2 ) RETURN BOOLEAN IS BEGIN RETURN g_names_used.EXISTS ( value_in ); END string_in_use; PROCEDURE mark_as_used (value_in IN VARCHAR2) IS BEGIN g_names_used ( value_in ) := TRUE; END mark_as_used; END string_tracker;

Copyright 2011 Feuerstein and Associates

string_tracker1.*

Page 52

Oracle PL/SQL Programming

Conclusions - String Indexed Arrays


Collections are, in many ways, like tables. We don't like full table scans (for the most part) and we don't like full collection scans. So if you ever find yourself writing a loop to iterate through all elements of the collection to find a match, consider changing your index type.
Or you could also consider defining a second collection to serve as an index into the first!
Copyright 2011 Feuerstein and Associates

string_index.sql genaa.sql

Page 53

Oracle PL/SQL Programming

Manipulating Nested Tables as Multisets


Nested tables are, from a theoretical standpoint, "multisets."
There is no inherent order to the elements. Duplicates are allowed and are significant. Relational tables are multisets as well.

If a set has no order, then it has no index, so it must be manipulated as a set. In Oracle Database 10g, Oracle added MULTISET set operators to manipulate the contents of nested tables (only).
Use in both PL/SQL blocks and SQL statements.
Copyright 2011 Feuerstein and Associates Page 54

Oracle PL/SQL Programming

Set-Oriented Features for Nested Tables


Determine if...
two nested tables are equal/unequal; a nested table has duplicates, and remove duplicates; one nested table contains another; an element is a member of a nested table

Perform set operations.


Join contents of two nested tables: MULTISET UNION. Return common elements of two nested tables with MULTISET INTERSECT. Take away the elements of one nested table from another with MULTISET EXCEPT (oddly, not MINUS).

Will perform much faster than writing your own algorithm to do the same thing.
Copyright 2011 Feuerstein and Associates Page 55

Oracle PL/SQL Programming

Check for equality and inequality


You can use = and <> to compare the contents of two nested tables.
But watch out! NULLs have the usual disruptive impact.

This is an enormous advantage over writing a program to compare the contents of two collections.
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; END; 10g_compare.sql 10g_compare_nulls.sql Copyright 2000-2008 Steven Feuerstein - Page 56 10g_compare_old.sql Copyright 2011 Feuerstein and Associates

Page 56

Oracle PL/SQL Programming

Nested table duplicates detection and removal


Use the SET operator to work with distinct values, and determine if you have a set of distinct values.
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;
Copyright 2000-2008 Steven Feuerstein - Page 57 Copyright 2011 Feuerstein and Associates

authors.pkg 10g_set.sql

Page 57

Oracle PL/SQL Programming

Determine if value is in nested table


Use the MEMBER OF syntax to determine if a value is in the nested table.
Much simpler than scanning the contents of a collection. Performs an equality check for the entire element; you cannot compare individual fields of records, and so on.

The implementation in SQL itself is quite slow. Performance in PL/SQL is fast.


Copyright 2011 Feuerstein and Associates

in_clause.* 10g_member_of.sql

Page 58

Oracle PL/SQL Programming

Does one nested table contains another?


The SUBMULTISET OF operator determines if all the elements of one nested table are in another.
DECLARE TYPE nested_typ IS TABLE OF NUMBER; nt1 nested_typ := nested_typ (1, 2); nt2 nested_typ := nested_typ (3, 2, 1); BEGIN IF nt1 SUBMULTISET OF nt3 THEN ... END IF; END; /

Copyright 2011 Feuerstein and Associates

authors.pkg 10g_submultiset.sql

Page 59

Oracle PL/SQL Programming

UNION two nested tables together


Use UNION to join together the contents of two nested tables. Duplicates are preserved unless you include the DISTINCT modifier.
This is the opposite of SQL UNION and UNION ALL.

The resulting collection is either empty or sequentially filled from index value 1.
You do not need to initialize or extend first.
authors.pkg 10g_union.sql

Copyright 2011 Feuerstein and Associates

Page 60

Oracle PL/SQL Programming

Intersect two nested tables together


Use INTERSECT to find the common elements of two nested tables. Duplicates are preserved unless you include the DISTINCT modifier.
And the ALL modifier is the default.

The resulting collection is either empty or sequentially filled from index value 1.
You do not need to initialize or extend first.

Copyright 2011 Feuerstein and Associates

10g_intersect.sql

Page 61

Oracle PL/SQL Programming

Take away the elements of one nested table from another Use EXCEPT (not MINUS!) to take all elements in one nested table out of another. Duplicates are preserved unless you include the DISTINCT modifier.
And the ALL modifier is the default.

The resulting collection is either empty or sequentially filled from index value 1.
You do not need to initialize or extend first.
Copyright 2011 Feuerstein and Associates

10g_except.sql

Page 62

Oracle PL/SQL Programming

Conclusions MULTISET operators


When you need to manipulate the contents of a collection as a set, use a nested table. The MULTISET operators offer a powerful, simple way to avoid writing lots of code. The SET, SUBMULTISET and MEMBER operators also can come in very handy. Watch out for results when your nested table may contain NULL elements.

Copyright 2011 Feuerstein and Associates

Page 63

Oracle PL/SQL Programming

Collections vs. Global Temporary Tables


Global temporary tables cut down on the overhead of working with persistent tables.
And you can use the full power of SQL, which is their main advantage over collections.

GTTs still require interaction with the SGA. So collections will still be faster, but they will use more memory.
GTTs consume SGA memory.

Copyright 2011 Feuerstein and Associates

global_temp_tab_vs_coll.sql

Page 64

Oracle PL/SQL Programming

Whats the Problem?


We have, on average, 10,000 employees per department.
CREATE OR REPLACE PROCEDURE upd_for_dept ( dept_in IN employee.department_id%TYPE ,newsal_in IN employee.salary%TYPE) IS CURSOR emp_cur IS SELECT employee_id,salary,hire_date FROM employee WHERE department_id = dept_in; BEGIN FOR rec IN emp_cur LOOP

adjust_compensation (rec, newsal_in);


UPDATE employee SET salary = rec.salary WHERE employee_id = rec.employee_id; END LOOP; END upd_for_dept;

Copyright 2011 Feuerstein and Associates

Page 65

Oracle PL/SQL Programming

Row-by-row = Slow-by-slow?
Many PL/SQL blocks execute the same SQL statement repeatedly, with different bind values. Retrieves data one row at a time. Performs same DML operation for each row retrieved. The SQL engine does a lot to optimize performance, but you this row-by-row processing is inherently slow.
But, but...aren't SQL and PL/SQL supposed to be very tightly integrated? Let's take a look "under the covers.
Copyright 2011 Feuerstein and Associates Page 66

Oracle PL/SQL Programming

Repetitive statement processing from PL/SQL


Oracle server
PL/SQL Runtime Engine
PL/SQL block
FOR rec IN emp_cur LOOP UPDATE employee SET salary = ... WHERE employee_id = rec.employee_id; END LOOP;

SQL Engine

Procedural statement executor

SQL statement executor

Performance penalty for many context switches


Copyright 2011 Feuerstein and Associates Page 67

Oracle PL/SQL Programming

Bulk Processing in PL/SQL


The goal is straightforward: reduce the number of context switches and you improver performance. To do this, Oracle "bundles up" the requests for data (or to change data) and then passes them with a single context switch. FORALL speeds up DML.
Use with inserts, updates, deletes and merges. Move data from collections to tables.

BULK COLLECT speeds up queries.


Can be used with all kinds of queries: implicit, explicit, static and dynamic. Move data from tables into collections.

Copyright 2011 Feuerstein and Associates

Page 68

Oracle PL/SQL Programming

Bulk processing with FORALL


Oracle server
PL/SQL Runtime Engine
PL/SQL block
FORALL indx IN list_of_emps.FIRST.. list_of_emps.LAST UPDATE employee SET salary = ... WHERE employee_id = list_of_emps(indx); Update... Update... Update... Update... Update... Update...

SQL Engine

Procedural statement executor

SQL statement executor


Update... Update... Update... Update... Update... Update...

Fewer context switches, same SQL behavior

Copyright 2011 Feuerstein and Associates

Page 69

Oracle PL/SQL Programming

Impact of Bulk Processing in SQL layer


The bulk processing features of PL/SQL change the way the PL/SQL engine communicates with the SQL layer. For both FORALL and BULK COLLECT, the processing in the SQL engine is almost completely unchanged.
Same transaction and rollback segment management Same number of individual SQL statements will be executed.

Only one difference: BEFORE and AFTER statementlevel triggers only fire once per FORALL INSERT statements.
Not for each INSERT statement passed to the SQL engine from the FORALL statement.
Copyright 2011 Feuerstein and Associates

statement_trigger_and_forall.sql

Page 70

Oracle PL/SQL Programming

BULK COLLECT Agenda


Introduction to BULK COLLECT Unlimited BULK COLLECTs Using the LIMIT clause When to convert to BULK COLLECT

Copyright 2011 Feuerstein and Associates

Page 71

Oracle PL/SQL Programming

BULK COLLECT for multi-row querying


SELECT * BULK COLLECT INTO collection(s) FROM table; FETCH cur BULK COLLECT INTO collection(s); EXECUTE IMMEDIATE query BULK COLLECT INTO collection(s);

Retrieve multiple rows into a collection with a single fetch (context switch to the SQL engine). Deposit the multiple rows of data into one or more collections.

Copyright 2011 Feuerstein and Associates

Page 72

Oracle PL/SQL Programming

"Good to Know" about BULK COLLECT


NO_DATA_FOUND is not raised when no rows

are fetched; instead, the collection is empty. The "INTO" collections are filled sequentially from index value 1.
There are no "gaps" between 1 and the index value returned by the COUNT method.

Only integer-indexed collections may be used. No need to initialize or extend nested tables and varrays. Done automatically by Oracle.
Copyright 2011 Feuerstein and Associates Page 73

Oracle PL/SQL Programming

BULK COLLECT for Implicit Cursor


Declare a nested table of records to hold the queried data. Fetch all rows into collection sequentially, starting with 1.
DECLARE TYPE employees_aat IS TABLE OF employees%ROWTYPE; l_employees employees_aat; BEGIN SELECT * BULK COLLECT INTO l_employees FROM employees;

bulkcoll.sql bulkcollect.tst

Iterate through the collection contents with a loop.

FOR indx IN 1 .. l_employees.COUNT LOOP process_employee (l_employees(indx)); END LOOP; END;

But what if I need to fetch and process millions of rows? This approach could consume unacceptable amounts of PGA memory.
Page 74

Copyright 2011 Feuerstein and Associates

Oracle PL/SQL Programming

What's the problem with this code?


DECLARE TYPE employees_aat IS TABLE OF employees%ROWTYPE; l_employees employees_aat; BEGIN SELECT * BULK COLLECT INTO l_employees FROM employees; FOR indx IN 1 .. l_employees.COUNT LOOP process_employee (l_employees(indx)); END LOOP; END;

As data volume grows, PGA memory requirement grows. Eventually....KABOOM!


Copyright 2011 Feuerstein and Associates Page 75

Oracle PL/SQL Programming

Limiting retrieval with BULK COLLECT


If you are certain that your table with never have more than N rows, use a VARRAY (N) to hold the fetched data.
If that limit is exceeded, Oracle will raise an error. This is not, however, a very common scenario.

If you do not know in advance how many rows you might retrieve, you should:
1. Declare an explicit cursor. 2. Fetch BULK COLLECT with the LIMIT clause.
Copyright 2011 Feuerstein and Associates Page 76

Oracle PL/SQL Programming

Limit 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 emps_in_dept_cur%ROWTYPE; emps emp_tt; BEGIN OPEN emps_in_dept_cur; LOOP Use the LIMIT clause with the FETCH emps_in_dept_cur INTO to manage the amount of BULK COLLECT INTO emps LIMIT 1000; memory used with the BULK EXIT WHEN emps.COUNT = 0; process_emps (emps); END LOOP; CLOSE emps_in_dept_cur; END bulk_with_limit;
bulklimit.sql
Copyright 2011 Feuerstein and Associates Page 77

COLLECT operation.

Definitely the preferred approach in production applications with large or varying datasets.

Oracle PL/SQL Programming

Details on that LIMIT clause


The limit value can be a literal or a variable.
I suggest using passing the limit as a parameter to give you maximum flexibility.

A limit of 100 seems like a good default value.


Setting it to 500 or 1000 doesn't seem to make much difference in performance.

With very large volumes of data and small numbers of batch processes, however, a larger LIMIT could help.
Copyright 2011 Feuerstein and Associates Page 78

Oracle PL/SQL Programming

Terminating loops containing BULK COLLECT


LOOP FETCH my_cursor BULK COLLECT INTO l_collection LIMIT 100; EXIT WHEN my_cursor%NOTFOUND; BAD IDEA

You will need to break the habit of checking %NOTFOUND right after the fetch.
You might skip processing some of your data.

Instead, do one of the following:


At the end of the loop, check %NOTFOUND. Right after fetch, exit when collection.COUNT = 0. At end of loop, exit when collection.COUNT < limit.
bulklimit_stop.sql
Copyright 2011 Feuerstein and Associates Page 79

Oracle PL/SQL Programming

When to convert to BULK COLLECT


Prior to Oracle10g, you should convert all multiple row fetch code to BULK COLLECTs. On 10.1 and higher, the optimizer will automatically optimize cursor FOR loops to run at performance levels similar to BULK COLLECT. So leave your cursor for loops in place if they...
contain no DML operations. seem to be running fast enough.

Explicit BULK COLLECTs will usually run a little faster than cursor for loops optimized to BC.
10g_optimize_cfl.sql
Copyright 2011 Feuerstein and Associates Page 80

Oracle PL/SQL Programming

BULK COLLECT Conclusions


BULK COLLECT improves performance of queries that retrieve more than one row. Use the LIMIT clause to avoid excessive PGA memory consumption. Leave it to the optimizer to speed up "read only" cursor FOR loops.

Copyright 2011 Feuerstein and Associates

Page 81

Oracle PL/SQL Programming

FORALL Agenda
Introduction to FORALL Using the SQL%BULK_ROWCOUNT Referencing fields of collections of records Using FORALL with sparsely-filled collections Handling errors raised during execution of FORALL

Copyright 2011 Feuerstein and Associates

Page 82

Oracle PL/SQL Programming

Use FORALL for repeated DML operations


PROCEDURE upd_for_dept (...) IS BEGIN FORALL indx IN low_value .. high_value UPDATE employee SET salary = newsal_in WHERE employee_id = list_of_emps (indx); END; Bind array

Convert loops that contain inserts, updates, deletes or merges to FORALL statements. Header looks identical to a numeric FOR loop.
Implicitly declared integer iterator At least one "bind array" that uses this iterator as its index value. You can also use a different header "style" with INDICES OF and VALUES OF (covered later)
Copyright 2011 Feuerstein and Associates

forall_timing.sql forall_examples.sql

Page 83

Oracle PL/SQL Programming

More on FORALL
Use any type of collection with FORALL. Only one DML statement is allowed per FORALL. Each FORALL is its own "extended" DML statement. The collection must be indexed by integer. The bind array must be sequentially filled. Unless you use the INDICES OF or VALUES OF clause. Indexes cannot be expressions. forall_restrictions.sql
Copyright 2011 Feuerstein and Associates Page 84

Oracle PL/SQL Programming

How many rows were modified?


SQL%ROWCOUNT returns total number of rows modified by entire FORALL.
Not to be relied on when used with LOG ERRORS.

Use the SQL%BULK_ROWCOUNT cursor attribute to determine how many rows are modified by each statement.
A "pseudo-collection" of integers; no methods are defined for this element.
bulk_rowcount.sql
Copyright 2011 Feuerstein and Associates Page 85

11g

Oracle PL/SQL Programming

FORALL and collections of records


Prior to 11g, you cannot reference a field of a record in FORALL. You must instead break data into separate collections, or... You can also perform record-level inserts and updates. In 11g, this restriction is lifted (but it is an undocumented feature).
11g_field_of_record.sql
Copyright 2011 Feuerstein and Associates Page 86

Oracle PL/SQL Programming

Using FORALL with Sparse Collections


Prior to Oracle10g R2, the binding arrays in a FORALL statement must be sequentially filled. Now, however, you can bind sparse collections by using INDICES OF and VALUES OF in the FORALL header.
PROCEDURE upd_for_dept (...) IS BEGIN FORALL indx IN INDICES OF list_of_emps UPDATE employee SET salary = newsal_in WHERE employee_id = list_of_emps (indx);
10g_indices_of*.sql 10g_values_of*.sql

Copyright 2011 Feuerstein and Associates

Page 87

Oracle PL/SQL Programming

FORALL and DML Errors


FORALLs typically execute a large number of DML statements. When an exception occurs in one of those DML statement, the default behavior is:
That statement is rolled back and the FORALL stops. All (previous) successful statements are not rolled back.

What if you want the FORALL processing to continue, even if an error occurs in one of the statements? Just add the SAVE EXCEPTIONS clause!
Copyright 2011 Feuerstein and Associates Page 88

Oracle PL/SQL Programming

SAVE EXCEPTIONS and FORALL


PROCEDURE upd_for_dept (newsal_in IN NUMBER, list_of_emps_in IN DBMS_SQL.NUMBER_TABLE) IS BEGIN FORALL indx IN list_of_emps_in.FIRST .. list_of_emps_in.LAST SAVE EXCEPTIONS UPDATE employees SET salary = newsal_in WHERE employee_id = list_of_emps_in (indx); END;

The SAVE EXCEPTIONS clause tells Oracle to save exception information and continue processing all of the DML statements. When the FORALL statement completes, if at least one exception occurred, Oracle then raises ORA-24381. You then check the contents of SQL%BULK_EXCEPTIONS.
Copyright 2011 Feuerstein and Associates Page 89

Oracle PL/SQL Programming

Example: FORALL with SAVE EXCEPTIONS


Add SAVE EXCEPTIONS to enable FORALL to suppress errors at the statement level.
CREATE OR REPLACE PROCEDURE load_books (books_in IN book_obj_list_t) IS bulk_errors EXCEPTION; PRAGMA EXCEPTION_INIT ( bulk_errors, -24381 ); BEGIN Allows processing of all FORALL indx IN books_in.FIRST..books_in.LAST statements, even after SAVE EXCEPTIONS an error occurs. INSERT INTO book values (books_in(indx)); EXCEPTION WHEN bulk_errors THEN Iterate through FOR indx in 1..SQL%BULK_EXCEPTIONS.COUNT "pseudo-collection" LOOP log_error (SQL%BULK_EXCEPTIONS(indx).ERROR_INDEX of errors. , SQL%BULK_EXCEPTIONS(indx).ERROR_CODE); END LOOP; END;
bulkexc.sql
Copyright 2011 Feuerstein and Associates Page 90

If any exception is encountered, Oracle raises 24381 when done.

Oracle PL/SQL Programming

SAVE EXCEPTIONS in Detail


For each exception raised, Oracle populates the SQL%BULK_EXCEPTIONS pseudo-collection of records.
The record has two fields : ERROR_INDEX and ERROR_CODE. ERROR_INDEX: the index in the bind array for which the error occurred. ERROR_CODE: the number (positive) for the error that was raised

It's a pseudo-collection, because it only supports a single method: COUNT. So you iterate from 1 to SQL%BULK_EXCEPTIONS.COUNT to get information about each error. Unfortunately, it does not store the error message.
Copyright 2011 Feuerstein and Associates Page 91

Oracle PL/SQL Programming

Converting to Bulk Processing


Let's take a look at the process by which you go from "old-fashioned" code to a bulk processing-based solution. From integrated row-by-row to phased processing Challenges include:
With multiple DML statements in loop, how do you "communicate" from one to the other? Avoid excessive PGA consumption
Copyright 2011 Feuerstein and Associates Page 92

Oracle PL/SQL Programming

The "Old Fashioned" Approach


Cursor FOR loop with two DML statements, trap exception, and keep on going.
CREATE OR REPLACE PROCEDURE upd_for_dept ( dept_in IN employees.department_id%TYPE , newsal_in IN employees.salary%TYPE) IS CURSOR emp_cur ...; BEGIN FOR rec IN emp_cur LOOP BEGIN INSERT INTO employee_history ... adjust_compensation (rec.employee_id, rec.salary); UPDATE employees SET salary = rec.salary ... EXCEPTION WHEN OTHERS THEN log_error; END; END LOOP; END upd_for_dept;
cfl_to_bulk_0.sql
Copyright 2011 Feuerstein and Associates Page 93

Oracle PL/SQL Programming

A phased approach with bulk processing


Change from integrated, row-by-row approach to a phased approach.
Relational Table Phase 1: Bulk collect from table(s) to collection

Phase 2: Modify contents of collection according to requirements

Relational Table Phase 3: FORALL from collection to table


Copyright 2011 Feuerstein and Associates Page 94

Oracle PL/SQL Programming

Translating phases into code


The cfl_to_bulk_5.sql file contains the converted program, following the phased approach.
Phase 1: Get Data Phase 3: Push Data Phase 2: Massage Data

BEGIN OPEN employees_cur;


LOOP fetch_next_set_of_rows ( bulk_limit_in, employee_ids, salaries, hire_dates); EXIT WHEN employee_ids.COUNT = 0; insert_history; adj_comp_for_arrays (employee_ids, salaries); update_employee; END LOOP; END upd_for_dept;

Phase 3: Push Data

Copyright 2011 Feuerstein and Associates

cfl_to_bulk_0.sql cfl_to_bulk_5.sql

Page 95

Oracle PL/SQL Programming

Conclusions Bulk Processing


FORALL is the most important performance tuning feature in PL/SQL.
Almost always the fastest way to execute repeated SQL operations in PL/SQL.

You trade off increased complexity of code for dramatically faster execution.
But remember that Oracle will automatically optimize cursor FOR loops to BULK COLLECT efficiency. No need to convert unless the loop contains DML or you want to maximally optimize your code.

Watch out for the impact on PGA memory!

Copyright 2011 Feuerstein and Associates

Page 96

Oracle PL/SQL Programming

Table Functions
A table function is a function that you can call in the FROM clause of a query, and have it be treated as if it were a relational table. Table functions allow you to perform arbitrarily complex transformations of data and then make that data available through a query: "just" rows and columns!
After all, not everything can be done in SQL.

Table functions can also help improve performance in several ways.

Copyright 2011 Feuerstein and Associates

Page 97

Oracle PL/SQL Programming

When should you use a table function?


To pass datasets back to a non-PL/SQL host environment, such as Java and .Net.
They just deal with rows and columns of data. To do this, you will need to take advantage of cursor variables.

Improve query performance with pipelined table functions.


For parallel query environments (data warehouse) And to reduce user perceptions of elapsed time
Copyright 2011 Feuerstein and Associates Page 98

Oracle PL/SQL Programming

Short tangent: Cursor variables and the OPEN FOR


A cursor variable is a variable that points to a cursor's result set (rows and columns of data). The type of a cursor variables is a REF CURSOR.
Strong REF CURSOR: select lists must match Weak REF CURSOR: use with any select.

Cursor variables can be passed as an argument to a program.


Or passed back to Java, .Net.
ref_cursors.sql
Copyright 2011 Feuerstein and Associates Page 99

Oracle PL/SQL Programming

Building a table function


A table function must return a nested table or varray based on a schema-defined type.
Types defined in a PL/SQL package can only be used with pipelined table functions.

The function header and the way it is called must be SQL-compatible: all parameters use SQL types; no named notation allowed until 11g.
In some cases (streaming and pipelined functions), the IN parameter must be a cursor variable -- a query result set.

Copyright 2011 Feuerstein and Associates

Page 100

Oracle PL/SQL Programming

Simple table function example


Return a list of names as a nested table, and then call that function in the FROM clause.
CREATE OR REPLACE FUNCTION lotsa_names ( base_name_in IN VARCHAR2, count_in IN INTEGER ) RETURN names_nt SELECT column_value IS FROM TABLE ( retval names_nt := names_nt (); lotsa_names ('Steven' BEGIN , 100)) names; retval.EXTEND (count_in); FOR indx IN 1 .. count_in LOOP retval (indx) := base_name_in || ' ' || indx; END LOOP; RETURN retval; END lotsa_names;
Copyright 2011 Feuerstein and Associates

COLUMN_VALUE -----------Steven 1 ... Steven 100

tabfunc_scalar.sql
Page 101

Oracle PL/SQL Programming

Streaming data with table functions


You can use table functions to "stream" data through several stages within a single SQL statement.
Example: transform one row in the stocktable to two rows in the tickertable.
CREATE TABLE stocktable ( ticker VARCHAR2(20), trade_date DATE, open_price NUMBER, close_price NUMBER ) / CREATE TABLE tickertable ( ticker VARCHAR2(20), pricedate DATE, pricetype VARCHAR2(1), price NUMBER) /

tabfunc_streaming.sql

Copyright 2011 Feuerstein and Associates

Page 102

Oracle PL/SQL Programming

Streaming data with table functions - 2


In this example, transform each row of the stocktable into two rows in the tickertable.
CREATE OR REPLACE PACKAGE refcur_pkg IS TYPE refcur_t IS REF CURSOR RETURN stocktable%ROWTYPE; END refcur_pkg; / CREATE OR REPLACE FUNCTION stockpivot (dataset refcur_pkg.refcur_t) RETURN tickertypeset ...

BEGIN INSERT INTO tickertable SELECT * FROM TABLE (stockpivot (CURSOR (SELECT * FROM stocktable))); END; / tabfunc_streaming.sql

Copyright 2011 Feuerstein and Associates

Page 103

Oracle PL/SQL Programming

Use pipelined functions to enhance performance.


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

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 only be called within a SQL statement.


They make no sense within non-multi-threaded PL/SQL blocks.
Copyright 2011 Feuerstein and Associates Page 104

Oracle PL/SQL Programming

Applications for pipelined functions


Execution functions in parallel.
In Oracle9i Database Release 2 and above, use the PARALLEL_ENABLE clause to allow your pipelined function to participate fully in a parallelized query. Critical in data warehouse applications.

Improve speed of delivery of data to web pages.


Use a pipelined function to "serve up" data to the webpage and allow users to begin viewing and browsing, even before the function has finished retrieving all of the data.

And pipelined functions use less PGA memory than non-pipelined functions!

Copyright 2011 Feuerstein and Associates

Page 105

Oracle PL/SQL Programming

Piping rows out from a pipelined function


Add PIPELINED keyword to header 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.sql tabfunc_pipelined.sql

Pipe a row of data back to calling block or query

RETURN...nothing at all!

Copyright 2011 Feuerstein and Associates

Page 106

Oracle PL/SQL Programming

Enabling Parallel Execution


You can use pipelined functions with the Parallel Query option to avoid serialization of table function execution. 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]} )
Copyright 2011 Feuerstein and Associates Page 107

Oracle PL/SQL Programming

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.

Copyright 2011 Feuerstein and Associates

Page 108

Oracle PL/SQL Programming

The NOCOPY hint


By default, Oracle passes all IN OUT and OUT arguments by value, not reference.
This means that OUT and IN OUT arguments always involve some copying of data.

With NOCOPY, you turn off the copy process.


But it comes with a risk: Oracle will not automatically "rollback" or reverse changes made to your variables if the NOCOPY-ed program terminates with an unhandled exception.
nocopy*.* string_nocopy.*
Copyright 2011 Feuerstein and Associates Page 109

Oracle PL/SQL Programming

Optimizing Datatypes
All datatypes are not created equal. When performing intensive integer computations, consider PLS_INTEGER instead of INTEGER. When performing computations with floating point values, consider the BINARY_DOUBLE or BINARY_FLOAT types. Remember, for most situations, the impact will not be noticeable.
Copyright 2011 Feuerstein and Associates

integer_compare.sql binary_types.sql

Page 110

Oracle PL/SQL Programming

Suppressing Errors with LOG ERRORS


Why cover this in a performance training? Because the raising and trapping of an exception slows down a program dramatically.
If it doesn't simply stop it completely.

When changing a large number of rows, you might want to continue past errors, to get as much work done as possible.
LOG ERRORS allows you to do this even when a DML statement fails.
Copyright 2011 Feuerstein and Associates Page 111

Oracle PL/SQL Programming

Impact of errors on DML execution


A single DML statement can result in changes to multiple rows. When an error occurs on a change to a row....

Usually acceptable, but what if you want to:


Avoid losing all prior changes? Avoid the performance penalty of exception management in PL/SQL?

All previous changes from that statement are rolled back. No other rows are processed. An error is passed out to the calling block (turns into a PL/SQL exception). No rollback on completed DML in that session.

Copyright 2011 Feuerstein and Associates

errors_and_dml.sql

Page 112

Oracle PL/SQL Programming

Row-level Error Suppression in DML with LOG ERRORS


Once the error propagates out to the PL/SQL layer, it is too late; all changes to rows have been rolled back. The only way to preserve changes to rows is to add the LOG ERRORS clause in your DML statement.
Errors are suppressed at row level within the SQL Layer.

But you will first need to created an error log table with DBMS_ERRLOG.
Copyright 2011 Feuerstein and Associates Page 113

Oracle PL/SQL Programming

Terminology for LOG ERRORS feature


DML table: the table on which DML operations will be performed Error logging table (aka, error table): the table that will contain history of errors for DML table Reject limit: the maximum number of errors that are acceptable for a given DML statement
"If more than 100 errors occur, something is badly wrong, just stop."
Copyright 2011 Feuerstein and Associates Page 114

Oracle PL/SQL Programming

Step 1. Create an error log table


Call DBMS_ERRLOG.CREATE_ERROR_LOG to create the error logging table for your "DML table."
Default name: ERR$_<your_table_name>

You can specify alternative table name, tablespace, owner.


Necessary if DML table name > 25 characters!

The log table contains five standard error log info columns and then a column for each VARCHAR2-compatible column in the DML table.
Copyright 2011 Feuerstein and Associates

dbms_errlog.sql

Page 115

Oracle PL/SQL Programming

Step 2: Add LOG ERRORS to your DML


UPDATE employees SET salary = salary_in LOG ERRORS REJECT LIMIT UNLIMITED;

UPDATE employees SET salary = salary_in LOG ERRORS REJECT LIMIT 100;

Specify the limit of errors after which you want the DML statement to stop or UNLIMITED to allow it to run its course. Then...make sure to check the error log table after you run your DML statement!
Oracle will not raise an exception when the DML statement ends big difference from SAVE EXCEPTIONS.
Copyright 2011 Feuerstein and Associates Page 116

Oracle PL/SQL Programming

"Gotchas" in the LOG ERRORS feature


The default error logging table is missing some critical information.
When the error occurred, who executed the statement, where it occurred in my code

Error reporting is often obscure: "Table or view does not exist." Its up to you to grant the necessary privileges on the error log table. If the DML table is modified from another schema, that schema must be able to write to the log table as well. Use the DBMS_ERRLOG helper package to get around many of these issues.
Copyright 2011 Feuerstein and Associates

dbms_errlog.sql

Page 117

Oracle PL/SQL Programming

The DBMS_ERRLOG helper package


Creates the error log table. Adds three columns to keep track of user, timestamp and location in code. Compiles a trigger to populate the added columns. Creates a package to make it easier to manage the contents of the error log table.

Copyright 2011 Feuerstein and Associates

dbms_errlog_helper.sql dbms_errlog_helper_demo.sql

Page 118

Oracle PL/SQL Programming

LOG ERRORS Conclusions


When executing multiple DML statements or affecting multiple rows, decide on your error policy.
Stop at first error or continue?

Then decide on the level of granularity of continuation: statement or row?


LOG ERRORS is the only way to perform row-level error suppression.

Make sure that you check and manage any error logs created by your code.
Copyright 2011 Feuerstein and Associates Page 119

Oracle PL/SQL Programming

Optimizing SQL in PL/SQL (more!)


The RETURNING Clause Implicit-vs-explicit queries Optimizing Dynamic SQL Update large tables in parallel with DBMS_PARALLEL_EXECUTE

Copyright 2011 Feuerstein and Associates

Page 120

Oracle PL/SQL Programming

RETURNING Clause
Use the RETURNING INTO clause to retrieve information about rows modified by the DML statement.
Avoid a separate SELECT.

When more than one row is modified, use RETURNING BULK COLLECT INTO. You cannot return an entire row into a record.

Copyright 2011 Feuerstein and Associates

returning.tst method_2_returning.sql

Page 121

Oracle PL/SQL Programming

Implicit or Explicit - which is faster?


BEGIN SELECT INTO FROM WHERE last_name l_name employees employee_id = 128; DECLARE CURSOR name_cur IS SELECT last_name FROM employees WHERE employee_id = 128; BEGIN OPEN name_cur; FETCH name_cur INTO l_name;

Confusion lingers from ancient "guru" advice. Implicit cursors (aka, SELECT INTO) will almost always be the most efficient way to fetch a single row of data.
Even though it must perform a second fetch to check for TOO_MANY_ROWS, Oracle has optimized it to run faster than explicit cursors.
Copyright 2011 Feuerstein and Associates

single_row_fetch.sql

Page 122

Oracle PL/SQL Programming

Optimizing Dynamic SQL


Use native dynamic SQL instead of DBMS_SQL.
Less code, easier to maintain, more efficient.

Bind variables, rather than concatenate, whenever possible.


The code is simpler and easier to maintain Oracle will avoid repeated parsing.

Copyright 2011 Feuerstein and Associates

tabcount_dbms_sql .sf tabcount_nds.sf useconcat*.* usebinding.sp

Page 123

11g

Oracle PL/SQL Programming

DBMS_PARALLEL_EXECUTE (11.2)
Incrementally update data in a large table in parallel:
1. Group sets of rows in the table into smaller chunks. 2. Apply the desired UPDATE statement to the chunks in parallel, committing each time you have finished processing a chunk.

Improves performance, reduces rollback space consumption, and reduces the number of row locks held.
Copyright 2011 Feuerstein and Associates Page 124

11g

Oracle PL/SQL Programming

DBMS_PARALLEL_EXECUTE - continued
Define chunks of data to be processed in parallel. Specify those chunks by ROWID, a SQL query, a numeric column. Especially helpful in data warehousing environments. See May/June 2010 Oracle Magazine issue for more thorough introduction.
Copyright 2011 Feuerstein and Associates

11g_parallel_execute.sql

Page 125

11g

Oracle PL/SQL Programming

Other Oracle Database 11g New PL/SQL Features


SIMPLE_INTEGER and native compilation Triggers: FOLLOWS and compound trigger Dynamic SQL interoperability and completeness Use sequences in native PL/SQL The CONTINUE statement PL/Scope Referencing supertype methods Fine-grained dependency model
Copyright 2011 Feuerstein and Associates Page 126

11g

Oracle PL/SQL Programming

Native Compilation

The SIMPLE_INTEGER and real Native Compilation


With PLSQL_CODE_TYPE='Native' ('INTERPRETED is the default), Oracle will compile PL/SQL code down to machine code on all chip sets supported by Oracle. Use only for production; you cant debug native code. Oracle recommends that you recompile your entire code base (including STANDARD and built-in packages) using native compilation!

The new, faster SIMPLE_INTEGER:


Has a NOT NULL constraint Values wrap, they do not overflow Faster than PLS_INTEGER
ALTER SESSION SET PLSQL_CODE_TYPE = 'NATIVE';

Copyright 2011 Feuerstein and Associates

Page 127

Oracle PL/SQL Programming

Other Oracle11g New Features


Lets now take a look at new features in Oracle Database 11g that are not (as) directly related to performance.
Trigger enhancements Native access to NEXTVAL and CURRVAL PL/Scope Dynamic SQL enhancements And more...

Copyright 2011 Feuerstein and Associates

Page 128

11g

Oracle PL/SQL Programming

The Compound Trigger


Rather than manage multiple triggers on the same table, you can join all trigger operations into a single compound trigger.
Avoidance of mutating trigger errors is now much simpler and more straightforward.
mutating.sql 11g_compound_mutating.sql CREATE TRIGGER full_mfe_excuse_transaction FOR UPDATE ON mfe_customers COMPOUND TRIGGER ... declare variables here ...
BEFORE STATEMENT IS BEGIN ... END BEFORE STATEMENT; BEFORE ROW IS BEGIN ... END BEFORE ROW; AFTER ROW IS BEGIN ... END AFTER ROW; AFTER STATEMENT IS BEGIN ... END AFTER STATEMENT; END full_mfe_excuse_transaction;
Page 129

Copyright 2011 Feuerstein and Associates

11g

Oracle PL/SQL Programming

Specifying order of trigger firing


Prior to Oracle11g, when you defined more than one trigger on the same action (e.g., "before insert"), there was no guarantee of the order in which the triggers would fire. Now simply include a FOLLOWS clause:
CREATE OR REPLACE TRIGGER after_insert_validate BEFORE INSERT ON my_table FOR EACH ROW FOLLOWS after_insert_adjust BEGIN ... END;
multiple_triggers.sql trigger_conflict.sql
Copyright 2011 Feuerstein and Associates Page 130

11g

Oracle PL/SQL Programming

Use sequences in native PL/SQL!


You no longer have to select from dual to get the next value of a sequence!
Much more intuitive code Improvement in performance
CREATE OR REPLACE TRIGGER employees_bi_trg BEFORE INSERT ON employees FOR EACH ROW BEGIN :NEW.employee_id := my_seq.NEXTVAL; END; /

Copyright 2011 Feuerstein and Associates

11g_native_sequence.sql

Page 131

11g

Oracle PL/SQL Programming

Using CONTINUE in a loop


You can now tell PL/SQL to terminate execution of the current loop body and immediately go on to the next iteration of that loop.
BEGIN <<outer_loop >> FOR o_index IN 1 .. my_list.COUNT LOOP <<inner_loop>> FOR i_index IN your_list.FIRST .. your_list.LAST LOOP ... lots of code /* Skip the rest of this and the outer loop if condition is met. */ CONTINUE outer_loop WHEN condition_is_met; ... more inner loop logic END LOOP inner_loop; ... more outer loop logic END LOOP outer_loop; END;
Copyright 2011 Feuerstein and Associates

11g_continue.sql local_modules_with_continue.sql

Page 132

11g

Oracle PL/SQL Programming

PL/Scope
A compiler-driven tool that collects information about identifiers and stores it in data dictionary views. Use PL/Scope to answer questions like:
Where is a variable assigned a value in a program? What variables are declared inside a given program? Which programs call another program (that is, you can get down to a subprogram in a package)? Find the type of a variable from its declaration.
Copyright 2011 Feuerstein and Associates Page 133

Oracle PL/SQL Programming

Getting Started with PL/Scope


ALTER SESSION SET plscope_settings='IDENTIFIERS:ALL'

PL/Scope must be enabled; it is off by default. When your program is compiled, information about all identifiers are written to the ALL_IDENTIFIERS view. You then query the contents of the view to get information about your code. Check the ALL_PLSQL_OBJECT_SETTINGS view for the PL/Scope setting of a particular program unit.
Copyright 2011 Feuerstein and Associates Page 134

Oracle PL/SQL Programming

Key Columns in ALL_IDENTIFIERS


TYPE
The type of identifier (VARIABLE, CONSTANT, etc.)

USAGE
The way the identifier is used (DECLARATION, ASSIGNMENT, etc.)

LINE and COL


Line and column within line in which the identifier is found

SIGNATURE
Unique value for an identifier. Especially helpful when distinguishing between overloadings of a subprogram or "connecting" subprogram declarations in package with definition in package body.

USAGE_ID and USAGE_CONTEXT_ID


Reveal hierarchy of identifiers in a program unit
Copyright 2011 Feuerstein and Associates Page 135

11g

Oracle PL/SQL Programming

Start with some simple examples


Show all the identifiers in a program unit Show all variables declared in a subprogram (not at package level) Show all variables declared in the package specifications Show the locations where a variable could be modified
plscope_demo_setup.sql plscope_all_idents.sql plscope_var_declares.sql plscope_gvar_declares.sql plscope_var_changes.sql

Copyright 2011 Feuerstein and Associates

Page 136

11g

Oracle PL/SQL Programming

More advanced examples


Find exceptions that are defined but never raised Show the hierarchy of identifiers in a program unit Validate naming conventions with PL/Scope

plscope_unused_exceptions.sql plscope_hierarchy.sql plscope_naming_conventions.sql

Copyright 2011 Feuerstein and Associates

Page 137

11g

Oracle PL/SQL Programming

PL/Scope Helper Utilities


Clearly, "data mining" in ALL_IDENTIFIERS can get very complicated. Suggestions for putting PL/Scope to use:
Build views to hide some of the complexity. Build packages to provide high-level subprograms to perform specific actions.

plscope_helper_setup.sql plscope_helper.pkg

Copyright 2011 Feuerstein and Associates

Page 138

11g

Oracle PL/SQL Programming

PL/Scope Summary
PL/Scope gives you a level of visibility into your code that was never before possible. The ALL_IDENTIFIERS view is not straightforward. Use the helper package to get you started. Hopefully we will see PL/Scope interfaces built into the most popular IDEs.

Copyright 2011 Feuerstein and Associates

Page 139

11g

Oracle PL/SQL Programming

Oracle11g Enhancements for Dynamic SQL


Parse and execute very large strings.
EXECUTE IMMEDIATE a CLOB DBMS_SQL.PARSE a CLOB

Interoperability
Convert DBMS_SQL cursor to cursor variable Convert cursor variable to DBMS_SQL cursor

Improved security
Random generation of DBMS_SQL cursor handles Denial of access/use of DBMS_SQL with invalid cursor or change of effective user.

Copyright 2011 Feuerstein and Associates

Page 140

11g

Oracle PL/SQL Programming

Parse very large SQL statements


Both EXECUTE IMMEDIATE and OPEN FOR now accept a CLOB. A new DBMS_SQL.PARSE overloading also accepts a CLOB. You no longer need to use the collection overloadings of DBMS_SQL.PARSE to work with very large strings.

Copyright 2011 Feuerstein and Associates

exec_ddl_from_file.sql exec_ddl_from_file_11g.sql

Page 141

Oracle PL/SQL Programming

Interoperability
DBMS_SQL.TO_REFCURSOR
Cursor handle to cursor variable Useful when you need DBMS_SQL to bind and execute, but easier to fetch through cursor variable.

DBMS_SQL.TO_CURSOR_NUMBER
Cursor variable to cursor handle Binding is static but SELECT list is dynamic

Copyright 2011 Feuerstein and Associates

Page 142

11g

Oracle PL/SQL Programming

DBMS_SQL.TO_REFCURSOR
Converts a SQL cursor number to a weak cursor variable, which you can use in native dynamic SQL statements. Before passing a SQL cursor number to the DBMS_SQL.TO_REFCURSOR function, you must OPEN, PARSE, and EXECUTE it (otherwise an error occurs). After you convert a SQL cursor number to a REF CURSOR variable, DBMS_SQL operations can access it only as the REF CURSOR variable, not as the SQL cursor number.
Using the DBMS_SQL.IS_OPEN function to see if a converted SQL cursor number is still open causes an error.
Copyright 2011 Feuerstein and Associates

11g_to_refcursor.sql

Page 143

11g

Oracle PL/SQL Programming

DBMS_SQL.TO_CURSOR_NUMBER
Converts a REF CURSOR variable (either strong or weak) to a SQL cursor number, which you can pass to DBMS_SQL subprograms. Before passing a REF CURSOR variable to the DBMS_SQL.TO_CURSOR_NUMBER function, you must OPEN it. After you convert a REF CURSOR variable to a SQL cursor number, native dynamic SQL operations cannot access it.
Copyright 2011 Feuerstein and Associates

11g_to_cursorid.sql

Page 144

11g

Oracle PL/SQL Programming

Improved Security
Cursor handles generated by the DBMS_SQL.OPEN_CURSOR function are now random and not sequential. Pass an invalid cursor handle to many DBMS_SQL programs and DBMS_SQL is then disabled.
Have to reconnect.

You can specify a security level for DBMS_SQL cursor management.


Minimize danger of SQL injection.
Copyright 2011 Feuerstein and Associates

11g_random_cursor_handle.sql 11g_access_denied_1.sql 11g_effective_user_id.sql

Page 145

11g

Oracle PL/SQL Programming

Oracle11g Dynamic SQL Conclusions


Both mechanisms for dynamic SQL have been improved. Of especial importance is the ability to move between native dynamic SQL and DBMS_SQL, to make it even easier to implement method 4 dynamic SQL requirements. Make sure you take full advantage of the security-related features of DBMS_SQL for any external-facing interfaces.
If, that is, you are required to use DBMS_SQL!
Copyright 2011 Feuerstein and Associates Page 146

11g

Oracle PL/SQL Programming

Referencing supertype methods


New to Oracle 11g, you can invoke a supertype's method in your override of that method.
Useful when you want to "add on" to supertype method, but you certainly don't want to have to copy/paste the code needed.

One very typical example is when you want to "display" an object.


Show values of attributes of each type in the hierarchy. Each "level" has its own "to _string" function.

11g_gen_invoc.sql
Copyright 2011 Feuerstein and Associates Page 147

Oracle PL/SQL Programming

Pre-Oracle11g Dependency Model


Dependencies tracked at object level
Which tables is a program dependent on? Which program units is a program dependent on?

So if any change is made to a referenced object, all dependent objects' status are set to INVALID.
Even if the change doesn't affect the dependent object.

Use ALL_DEPENDENCIES to analyze.


REFERENCED* columns show the objects on which an object depends.
Copyright 2011 Feuerstein and Associates

analyzedep*.* code_referencing_tables.sql layer_validator*.*

Page 148

Oracle PL/SQL Programming

Oracle11g Dependency Model


Now dependencies are tracked down to the sub-object level: "fine-grained dependencies" or FGD.
Columns within tables Parameters within program units.

Impact of change:
You can minimize invalidation of program units.

You cannot obtain this fine-grained dependency information through any data dictionary views yet.
11g_fgd*.sql
Copyright 2011 Feuerstein and Associates Page 149

Oracle PL/SQL Programming

High Performance PL/SQL - Conclusions


Tune your SQL first! Proactively apply all these features:
BULK COLLECT FORALL NOCOPY Function Result Cache RETURNING

Once you've identified bottlenecks, explore "secondary" optimization techniques.


Copyright 2011 Feuerstein and Associates Page 150

Oracle PL/SQL Programming

Make the Most of Oracle PL/SQL!


This language is not evolving very rapidly these days (less change than in SQL). Make sure that you are aware of key new (and existing) features, and put them to use. Always prioritize the maintainability of your code.
It's going to be around for YEARS to come!

Copyright 2011 Feuerstein and Associates

Page 151

S-ar putea să vă placă și