You are on page 1of 39

Oracle PL/SQL Best Practices

Best Practices for
Writing SQL
In Oracle PL/SQL
Steven Feuerstein
Quest Software,

Scope and Challenge
 This seminar will not review tuning tips for SQL
 It will offer suggestions on how best to write SQL
inside PL/SQL programs for maximum:
– Readability
– Maintainability
– Efficiency

What's the Big Deal?
 How you write SQL inside PL/SQL code is the most
critical factor affecting the quality of PL/SQL-based
 Consider:
– One of the reasons developers like PL/SQL so much is that
it is so easy to write SQL inside a PL/SQL block of code
– One of the most dangerous aspects of PL/SQL is that it is so
easy to write SQL inside a PL/SQL block of code

 Paradox? Irony? SQL is, in fact, a sort of Achilles
heel of PL/SQL development

Why We Write PL/SQL Code  The predominate reason you write PL/SQL programs is to interact with the database. which: – Is the repository of information that shapes your business – Is always changing  The layer of PL/SQL code should support the data model – It should not disrupt your ability to maintain and work with that model – Common coding practices tend to do just that: make it extremely difficult to modify and enhance your code as the data structures change  The difficulties surface in two different areas: – Transaction integrity – Poor coding practices .

how do you guarantee that each developer is going to get it right? givebonus1. a delete and six queries. two inserts.Transaction Integrity the Hard Way Typical Data Access Method Order Entry Program Application software access data structures directly Order Table Each program must maintain transaction integrity Item Table  When a transaction consists of three updates.sql .

ename INTO v_dname. BEGIN SELECT dname. ..The Dangers of Poor Coding Practices  If you are not very careful. This program is a ticking time bomb in my application. dept WHERE . v_ename CHAR(30). it is very easy to write your code in ways that cause your code to break whenever a change occurs in the underlying structures Data Structures Dept Emp Code PROCEDURE calc_totals IS v_dname VARCHAR2(20). END.. v_ename FROM emp....

SQL in PL/SQL Best Practices The View from 30. .000 Feet  Never repeat an SQL statement in application code.  Encapsulate all SQL statements behind a procedural interface. usually a package.  Take advantage of PL/SQL-specific enhancements for SQL.  Write your code assuming that the underlying data structures will change.

. then you probably repeat SQL. comes variation. – With repetition. – Potentially significant impact on performance and maintainability.. And sometimes you have to worry about more than logical variations! . and with it excessive parsing. and you have essentially lost control of your application code base  It is crucial that you avoid repetition of the same logical SQL statement.Never Repeat SQL  Take the "acid test" of SQL in PL/SQL: Can you say "Sim!" to the following question? Do you know all the places in your code where an INSERT (or DELETE or UPDATE) occurs for your critical table(s)? – If the answer is "not really".

Column B = = = select count(*) from after_deforestation. BEGIN update ceo_compensation set stock_options = 1000000. END. BEGIN update favorites set flavor = 'CHOCOLATE' where name = 'STEVEN'. END. BEGIN UPDATE favorites SET flavor = 'CHOCOLATE' WHERE name = 'STEVEN'. Takes With the Other  Oracle sometimes improves things in ways that make it very difficult for us to take advantage of them – Consider what it takes to avoid excessive parsing of SQL statements. salary = salary * 2. Column A SELECT COUNT(*) FROM after_deforestation. Compare the statements in Column A and Column B. salary = salary * 2 where layoffs > 10000. BEGIN UPDATE ceo_compensation SET stock_options = 1000000.0 WHERE layoffs > 10000. END. ? ? ? . END.Oracle Gives With One Hand.

sql 14 .Quiz: Count those cursors! What are the total number of cursors parsed by Oracle when those six statements (standalone SQL and PL/SQL statements) are executed? Choices: 6 9 10 12 cursor_quiz.

Analyzing the SGA with TOAD .

about these kinds of logical duplications? BEGIN UPDATE ceo_compensation SET stock_options = 1000000. salary = salary * 2 WHERE layoffs > 10000. stock_options = 1000000 where layoffs > 10000. you must be aware of the physical representation of your code – Pre-parsed cursors are only used for byte-wise equal statements (analyzed using a hash of the SQL string) – White space (blanks. tabs. BEGIN update ceo_compensation set salary = salary * 2. however.Crossing the Physical-Logical Divide  When you write SQL. . line breaks) make a difference – except when the SQL resides inside PL/SQL blocks – PL/SQL reformats SQL to avoid nuisance redundancy  What can be done.

) programs – You can't repeat it if you don't write it  Instead. rely on pre-built. not even write SQL in your PL/SQL (and Java and C and. SQL – "Hide" both individual SQL statements and entire transactions. use-often PL/SQL encapsulations of SQL statements. as a rule..How to Avoid SQL Repetition  You should.. write-once. With this approach you can virtually guarantee transaction integrity! . pre-tested.

sql .Transaction Integrity the PL/SQL Way A Method That Guarantees Integrity Order Entry Program All business rules are embedded and maintained in the package The application calls a packaged program to execute the logical transaction The packaged code communicates with the tables Order Table Item Table Oracle wrote PL/SQL for just this reason! givebonus2.

Hide all SQL Behind Procedural Interface  You can't watch over everybody's shoulders to "police" the construction of every SQL statement – You need to set policies and provide code to make it easy for developers to follow the rules – and write better code  Here are some recommendations: – – – – Build and use table encapsulation packages Hide all single row queries behind function interfaces In particular. don't expose the dual table Move multi-row cursors into packages .

) – If the encapsulation package doesn't have what you need.The Beauty of Table Encapsulation Employee Insert Update Delete GetRow Application Code  Store all of your SQL inside packages: one per table or "business object" – All DML statements written by an expert. add the new element. etc. so that everyone can take advantage of it – Could create separate packages for query-only and change-related functionality . behind a procedural interface. foreign key. with standardized exception handling – Commonly-needed cursors and functions to return variety of data (by primary key.

Encapsulation Package in DB Navigator .

Using Code Assistant to Deploy Encapsulations .

Check dependency information to identify program that rely directly on tables TRUE STORY! "I forced all programmers to use the encapsulated INSERT. 'FUNCTION') AND REFERENCED_type IN ('TABLE'. salary. SYSDATE). we determined that this one insert statement was executed over a million times! It has been in the SGA for over parsed one time and two weeks.Allow No Exceptions Instead of this: INSERT INTO employee (employee_id. .' || REFERENCED_name table_referenced FROM ALL_DEPENDENCIES WHERE type IN ('PACKAGE'. 10. instead of writing their own. salary_in => 10000." -. 'PACKAGE BODY'.' || name refs_table. Do this: te_employee. hire_date_in => SYSDATE). department_id. hire_date) VALUES (1005. 10000. 'PROCEDURE'. REFERENCED_owner || '. department_id_in => 10. never aging out because it is called so frequently. 'VIEW').Dan Clamage SELECT owner || '.insert ( employee_id_in => 1005. Using Quest’s SQLab.

Minimal Encapsulation a Must!  At an absolute minimum. hide every single row query behind the header of a function. – If you hide the query. you can choose (and change) the implementation for optimal performance. so you can take advantage of package-level data.  Best approach: put the function in a package. – Very useful for data caching mechanisms .

BEGIN SELECT last_name || '. DECLARE l_name te_employee. DECLARE l_name VARCHAR2(100)..fullname_t. FUNCTION fullname ( l employee.. BEGIN l_name := te_employee. END.last_name%TYPE... CREATE OR REPLACE PACKAGE te_employee AS SUBTYPE fullname_t IS VARCHAR2 (200)..' || first_name INTO l_name FROM employee WHERE employee_id = employee_id_in.employee_id%TYPE ) RETURN fullname_t. Instead of this. END. FUNCTION name ( employee_id_in IN ( employee_id_in)...first_name%TYPE ) RETURN fullname_t. END.Get Me the Name for an ID... f employee. / . . Instead of this.... .

. BEGIN l_employee_id := te_employee.next_pkey.. Write this: BEGIN SELECT employee_id_seq. Ever Expose the Dual Table  The dual table is 100% kluge. . It is astonishing that Oracle still relies on it within the STANDARD PL/SQL package  Always hide queries against the dual table inside a function – We need to be optimistic: perhaps in Oracle12i the dual table will no longer be necessary Instead of this.And Never.NEXTVAL INTO l_employee_id FROM dual.

Write Code Assuming Change Data structure changes Dependent programs marked invalid Re-compile invalid code Existing code base valid  Use anchoring to tightly link code to underlying data structures  Rely on bind variables inside SQL statements  Fetch into cursor records  Qualify all references to PL/SQL variables inside SQL statements .

2).ename%TYPE. totsales pkg.sales_amt%TYPE. Anchored Declarations v_ename emp. use anchored declarations rather than explicit datatype references – %TYPE for scalar structures – %ROWTYPE for composite structures Hard-Coded Declarations ename VARCHAR2(30).Anchor Declarations of Variables  You have two choices when you declare a variable: – Hard-coding the datatype – Anchoring the datatype to another structure  Whenever possible. emp_rec emp%ROWTYPE. tot_rec tot_cur%ROWTYPE. . totsales NUMBER (10.

BEGIN . 2). PACKAGE config IS dollar_amt NUMBER (10. .Oracle8i END config. .sps aq.pkg . SUBTYPE full_name IS VARCHAR2(100). SUBTYPE primary_key IS pkey_var%TYPE. -. pkey_var NUMBER(6).Examples of Anchoring DECLARE v_ename emp.dollar_amt%TYPE. END.ename%TYPE. The emp table ename VARCHAR2(60) empno NUMBER hiredate DATE sal NUMBER  Use %TYPE and %ROWTYPE when anchoring to database elements  Use SUBTYPEs for programmatically-defined types PLV. v_totsal config.primary_key. newid config.

always use %TYPE or %ROWTYPE.  Normalize/consolidate declarations of derived variables throughout your programs.Benefits of Anchoring  Synchronize PL/SQL variables with database columns and rows – If a variable or parameter does represent database information in your program. – Keeps your programs in synch with database structures without having to make code changes. – Make sure that all declarations of dollar amounts or entity names are consistent. Remember— Never Repeat Code . – Change one declaration and upgrade all others with recompilation.

The Many Faces of Hard-Coding 1 2 3 4 name VARCHAR2 (30). minbal NUMBER(10...2). 5 FETCH company_pkg.  Which of these six lines of code do not contain an example of hard-coding? (1-6) or (3 . BEGIN OPEN company_pkg. 6 IF name = ‘ACME’ THEN . minbal.6) or (3 and 5) or 3 .allrows INTO name.allrows (1507).

.2). BEGIN OPEN company_pkg. CLOSE company_pkg. CLOSE company_pkg.allrows.allrows.allrows. minbal. IF rec. Right rec company_pkg.allrows%ROWTYPE. FETCH company_pkg. BEGIN OPEN company_pkg.. it doesn't necessarily affect your code .allrows INTO rec.allrows INTO name..Fetch into Cursor Records! name VARCHAR2 (30) = ‘ACME’ THEN . Fetching into a record means writing less code If the cursor select list changes. minbal NUMBER(10. FETCH company_pkg. Wrong Fetching into individual variables hard-codes number of items in select list IF name = ‘ACME’ THEN .

name_cur (20).Avoid Hard-coding inside SQL  Don't bury hard-coded values in your SQL statements. hard to maintain code: DECLARE CURSOR marketing_cur IS SELECT last_name FROM employee WHERE department_id = 20.sql . DECLARE CURSOR r_and_d_cur IS SELECT last_name FROM employee WHERE department_id = 10. BEGIN OPEN marketing_cur. Local variables also avoid multiple parses BEGIN OPEN bydept. move your cursors to a shared area and then rely on that version in all instances  Here is some inefficient. – Instead. And what it should be: CREATE OR REPLACE PACKAGE bydept IS CURSOR name_cur (dept IN INTEGER) IS SELECT last_name FROM employee WHERE department_id = dept. bindvar.

is just a taste of the many things you can do to optimize SQL inside PL/SQL. of course. ..Write SQL Efficiently in PL/SQL  It's one thing to tune your SQL statements.. it is quite another to write your SQL inside PL/SQL so that it executes as efficiently as possible  We'll cover some of the most useful new features in Oracle8 and Oracle8i PL/SQL for improving SQL performance: – The RETURNING Clause – BULK BIND and COLLECT (Oracle8i)  This is.

.NEXTVAL. 'CHOCOLATE') RETURNING favorite_id.NEXTVAL. preference INTO l_favid.Use the RETURNING Clause  Oracle8 offers a new clause for INSERT and UPDATE statements: the RETURNING clause – Retrieve information from DML statement w/o a separate query Instead of this: BEGIN INSERT INTO favorites VALUES ( favorites_seq. 'ICE CREAM'. preference INTO l_favid. Do this: BEGIN INSERT INTO favorites VALUES ( favorites_seq. 'ICE CREAM'. 'CHOCOLATE'). l_flavor. END. l_flavor FROM flavors WHERE name = 'STEVEN' AND type = 'ICE CREAM'. END. SELECT favorite_id. 'STEVEN'. 'STEVEN'.

updating from a collection (or.FIRST. In Oracle8.LAST LOOP DELETE emp WHERE deptno = deptlist(aDept). / PROCEDURE reality_meets_dotcoms (deptlist dlist_t) IS BEGIN FOR aDept IN deptlist. in general. performing multi-row DML) meant writing code like this: CREATE TYPE dlist_t AS TABLE OF INTEGER. END. “Conventional bind” (and lots of them!) .deptlist. END LOOP..Use Bulk Binding and COLLECT  Oracle8i offers new syntax to improve the performance of both DML and queries.

END LOOP.Conventional Bind Oracle server PL/SQL Runtime Engine PL/SQL block FOR aDept IN deptlist.FIRST.LAST LOOP DELETE emp WHERE deptno = deptlist(aDept). deptlist.. Procedural statement executor SQL Engine SQL statement executor Performance penalty for many “context switches” .

FIRST.Enter the “Bulk Bind” Oracle server PL/SQL Runtime Engine PL/SQL block FORALL aDept IN deptlist.LAST DELETE emp WHERE deptno = deptlist(aDept).. deptlist. Procedura l statement executor SQL Engine SQL statement executor Much less overhead for context switching .

you can do this: PROCEDURE reality_meets_dotcoms (deptlist dlist_t) IS BEGIN FORALL aDept IN deptlist.Use the FORALL Bulk Bind Statement  Instead of the individual DML operations.LAST DELETE emp WHERE deptno = deptlist(aDept).  Some restrictions: – Only the single DML statement is allowed— If you want to INSERT and then UPDATE.FIRST.deptlist. two different FORALL statements – Cannot put an exception handler on the DML statement .. END.

COUNT). hiredates(i)). ename.LAST LOOP emplist(i) := emp_t(enos(i). enos numTab.depno%TYPE) RETURN emplist_t IS emplist emplist_t := emplist_t().FIRST. TYPE dateTab IS TABLE OF DATE. TYPE charTab IS TABLE OF VARCHAR2(12). . hiredate BULK COLLECT INTO enos. hdates FROM emp WHERE deptno = deptno_in.. names. END. names(i). FOR i IN enos. END LOOP. names charTab. emplist.Use BULK COLLECT for Queries  BULK COLLECT performs bulk bind of results from SQL select statement – Returns each selected expression in a table of scalars CREATE OR REPLACE FUNCTION get_a_mess_o_emps (deptno_in IN dept. BEGIN SELECT empno.enos. RETURN emplist. hdates dateTab.EXTEND(enos. TYPE numTab IS TABLE OF NUMBER.

prior successful DML statements are NOT ROLLED BACK  Bulk collects: – Can be used with implicit and explicit cursors – Collection is filled starting at row 1 .Tips and Fine Points  Use bulk binds if you write code with these characteristics: – 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 If error occurs.

Writing SQL in PL/SQL Summary  Never Repeat SQL – Maximize performance. Accept it and build "contingencies" into your code  Take advantage of PL/SQL's maturity as an extension to the SQL language. . data change. but you could also use Java)  Code for Change – Data structures change. minimize impact of change – Encapsulate your SQL statements behind a procedural interface (likely to be a PL/SQL package.

Discussion Presentation available at www.Q&A .com/presentations .quest.