Best Practices For Writing SQL in Oracle PLSQL
Best Practices For Writing SQL in Oracle PLSQL
Transaction Integrity
the Hard Way
Typical Data Access Method
Order Entry
Program
Application software
access data structures
directly
Order
Table
Item
Table
When a transaction consists of three updates, two inserts, a delete and six
queries, how do you guarantee that each developer is going to get it right?
givebonus1.sql
Emp
Code
PROCEDURE calc_totals
IS
v_dname VARCHAR2(20);
v_ename CHAR(30);
BEGIN
SELECT dname, ename
INTO v_dname, v_ename
FROM emp, dept
WHERE ...;
END;
This program is a
ticking time bomb
in my application...
If the answer is "not really", then you probably repeat SQL, and you
have essentially lost control of your application code base
Column B
=
select count(*)
from after_deforestation;
BEGIN
update favorites
set flavor = 'CHOCOLATE'
where name = 'STEVEN';
END;
BEGIN
update ceo_compensation
set stock_options = 1000000,
salary = salary * 2
where layoffs > 10000;
END;
?
?
10
12
cursor_quiz.sql
14
BEGIN
update ceo_compensation
set salary = salary * 2,
stock_options = 1000000
where layoffs > 10000;
SQL
Order
Table
Item
Table
givebonus2.sql
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, behind a procedural interface,
with standardized exception handling
Commonly-needed cursors and functions to return variety of data
(by primary key, foreign key, etc.)
If the encapsulation package doesn't have what you need, add the new element,
so that everyone can take advantage of it
Could create separate packages for query-only and change-related functionality
Allow No Exceptions
Instead of this:
INSERT INTO employee
(employee_id, department_id, salary, hire_date)
VALUES
(1005, 10, 10000, SYSDATE);
Do this:
te_employee.insert (
employee_id_in => 1005, department_id_in => 10,
salary_in => 10000, hire_date_in => SYSDATE);
TRUE STORY!
DECLARE
l_name te_employee.fullname_t;
BEGIN
l_name :=
te_employee.name (
employee_id_in);
...
END;
Instead of this....
CREATE OR REPLACE PACKAGE te_employee
AS
SUBTYPE fullname_t IS VARCHAR2
(200);
FUNCTION fullname (
l employee.last_name%TYPE,
f employee.first_name%TYPE
)
RETURN fullname_t;
FUNCTION name (
employee_id_in IN
employee.employee_id%TYPE
)
RETURN fullname_t;
END;
/
Write this:
BEGIN
SELECT employee_id_seq.NEXTVAL
INTO l_employee_id
FROM dual;
BEGIN
l_employee_id :=
te_employee.next_pkey;
Dependent
programs
marked invalid
Re-compile
invalid code
Existing
code base
valid
Hard-Coded Declarations
ename VARCHAR2(30);
totsales NUMBER (10,2);
Anchored Declarations
v_ename emp.ename%TYPE;
totsales pkg.sales_amt%TYPE;
emp_rec emp%ROWTYPE;
tot_rec tot_cur%ROWTYPE;
Examples of Anchoring
DECLARE
v_ename emp.ename%TYPE;
v_totsal config.dollar_amt%TYPE;
newid config.primary_key;
BEGIN
. . .
END;
PACKAGE config
IS
dollar_amt NUMBER (10, 2);
pkey_var NUMBER(6);
SUBTYPE primary_key
IS
pkey_var%TYPE;
SUBTYPE full_name IS
VARCHAR2(100); -- Oracle8i
END config;
VARCHAR2(60)
empno
NUMBER
hiredate
DATE
sal
NUMBER
Benefits of Anchoring
Synchronize PL/SQL variables with database columns and
rows
If a variable or parameter does represent database information in
your program, always use %TYPE or %ROWTYPE.
Keeps your programs in synch with database structures without
having to make code changes.
Wrong
Right
rec company_pkg.allrows%ROWTYPE;
BEGIN
OPEN company_pkg.allrows;
FETCH company_pkg.allrows INTO rec;
IF rec.name = ACME THEN ...
CLOSE company_pkg.allrows;
DECLARE
CURSOR r_and_d_cur IS
SELECT last_name FROM employee
WHERE department_id = 10;
Local variables
also avoid multiple parses
BEGIN
OPEN bydept.name_cur (20);
bindvar.sql
Do this:
BEGIN
INSERT INTO favorites VALUES (
favorites_seq.NEXTVAL, 'STEVEN', 'ICE CREAM', 'CHOCOLATE')
RETURNING favorite_id, preference INTO l_favid, l_flavor;
END;
Conventional Bind
Oracle server
PL/SQL Runtime Engine
PL/SQL block
FOR aDept IN deptlist.FIRST..
deptlist.LAST
LOOP
DELETE emp
WHERE deptno = deptlist(aDept);
END LOOP;
Procedural
statement
executor
SQL Engine
SQL
statement
executor
Performance
penalty for many
context switches
Procedura
l
statement
executor
SQL Engine
SQL
statement
executor
Some restrictions:
Only the single DML statement is allowed If you want to INSERT
and then UPDATE, two different FORALL statements
Cannot put an exception handler on the DML statement
Bulk collects:
Can be used with implicit and explicit cursors
Collection is filled starting at row 1
Q&A - Discussion
Presentation available at
www.quest.com/presentations