Professional Documents
Culture Documents
Best of Oracle PLSQL 11g PDF
Best of Oracle PLSQL 11g PDF
Sponsored by AMIS
Steven Feuerstein
steven@stevenfeuerstein.com
www.StevenFeuerstein.com
Oracle PL/SQL Programming
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
Conclusions
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.
Some Examples
... A + B ... T := A + B;
... T ... T is a generated variable. We never see
... it. And one operation is saved.
... A + B ... ...
... T ...
for i in 1 .. 10 loop
A := B + C;
...
end loop; Automatic relocation of a loop invariant.
Avoid repetitive computations.
A := B + C;
for i in 1 .. 10 loop
...
end loop;
10g_optimize_cfl.sql
Copyright 2011 Feuerstein and Associates Page 15
Oracle PL/SQL Programming
and then:
ALTER PROCEDURE bigproc COMPILE REUSE SETTINGS;
REM If you want to enable warning message number 06002 and all warnings in
REM the performance category, and treat 5005 as a "hard" compile error:
ALTER PROCEDURE my_procedure SET plsql_warnings =
'enable:06002', 'enable:performance', 'ERROR:05005';
plw6017.sql
plw5005.sql
Conditional Compilation
Compile selected parts of a program based on
conditions you provide with various compiler
directives.
Conditional compilation will allow you to:
Write code that will compile and run under different
versions of Oracle (relevant for future releases).
Run different code for test, debug and production
phases. That is, compile debug statements in and out of
your code.
Expose private modules for unit testing.
First released in Oracle Database 10g Release 2
Also implemented in later patch sets of 10gR1, plus 9iR2
(contact Oracle Support for details)
cc_postprocessed.sql
Copyright 2011 Feuerstein and Associates Page 35
Oracle PL/SQL Programming
cc_opt_level_check.sql
Using DBMS_DB_VERSION
This package, present in any Oracle Database
version supporting conditional compilation, contains
a set of Boolean constants showing absolute and
relative version information.
PROCEDURE insert_rows ( rows_in IN otn_demo_aat ) IS
BEGIN
$IF DBMS_DB_VERSION.VER_LE_10_1
$THEN
BEGIN
...
FORALL indx IN 1 .. l_dense.COUNT
INSERT INTO otn_demo VALUES l_dense (indx);
END;
$ELSE
FORALL indx IN INDICES OF rows_in
INSERT INTO otn_demo VALUES rows_in (indx);
$END
cc_bf_or_number.sql
Copyright 2011 Feuerstein and Associates cc_version_check.sql Page 37
Oracle PL/SQL Programming
PL/SQL Collections
Collections are single-dimensioned lists of
information, similar to 3GL arrays.
They are an invaluable data structure.
All PL/SQL developers should be very comfortable
with collections and use them often.
Collections take some getting used to.
They are not the most straightforward
implementation of array-like structures.
Advanced features like string indexes and multi-
level collections can be a challenge.
Agenda - Collections
Introduction and overview
Defining and using collection types
Using collection methods
Working with associative arrays
Working with nested tables
Working with varrays
Using collections inside SQL
Benefits of Non-Sequential Indexing
Using String Indexes with Associative Arrays
Working with Nested Collections
Using MULTISET Operators with Nested Tables
Best Practices for Collections
What is a collection?
A collection is an "ordered
group of elements, all of the
1 Apple
same type." (PL/SQL User
22 Pear
Guide) 100 Orange
In short, a list of "stuff"
Collections are similar to single-
10023 Apricot
dimensional arrays in other
programming languages.
With lots of subtle differences, as
well.
global_temp_tab_vs_coll.sql
Glossary of Terms
Element
A collection is made up of one or more elements, all of the same
type. Also referred to as "row."
Can be of almost any valid PL/SQL type.
Index value
The "location" in the collection in which an element is found. Also
referred to as "row number."
Usually an integer, can also be a string (associative arrays only)
Dense
Every index value between lowest and highest has a defined
element.
Sparse
One or more elements between lowest and highest index values
may be undefined (gaps).
Varray type
TYPE coll_name IS VARRAY (limit) OF element_type;
PROCEDURE my_procedure
IS
TYPE strings_t IS TABLE OF VARCHAR2(100);
Package-level Types
PACKAGE my_types
IS
TYPE strings_t IS TABLE OF VARCHAR2(100);
Schema-level Types
CREATE OR REPLACE TYPE strings_t IS TABLE OF VARCHAR2(100)
DECLARE
l_names my_types.string_t;
l_dates hire_dates_t;
l_dates HR.hire_dates_t;
l_strings DBMS_SQL.varchar2_table;
Collection Methods
The term method is used to describe
procedures and functions that defined in a class
or object type.
You invoke a method by attaching it, using dot
notation, to the name of the type/class or to an
instance of the class.
Collection methods are procedures and
functions that are attached to a collection
variable.
First introduction of object-oriented syntax in
PL/SQL way back in Oracle 7.3.4!
collection_exists.sql
Copyright 2011 Feuerstein and Associates plsqlloops.sp Page 58
Oracle PL/SQL Programming
BEGIN
FOR indx IN my_collection.FIRST .. my_collection.LAST
LOOP
do_something_with (my_collection (indx));
END LOOP;
END;
plsqlloops.sp
Copyright 2011 Feuerstein and Associates Page 61
Oracle PL/SQL Programming
Associative Arrays
DECLARE
TYPE list_of_names_t IS TABLE OF employees.last_name%TYPE
INDEX BY PLS_INTEGER;
happyfamily list_of_names_t;
l_index_value PLS_INTEGER := 88;
BEGIN
happyfamily (1) := 'Eli';
happyfamily (-15070) := 'Steven';
happyfamily (3) := 'Chris';
happyfamily (l_index_value) := 'Veva';
l_index_value := happyfamily.FIRST;
WHILE (l_index_value IS NOT NULL)
LOOP
DBMS_OUTPUT.put_line ( 'Value at index '
|| l_index_value
|| ' = '
|| happyfamily (l_index_value)
);
l_index_value := happyfamily.NEXT (l_index_value);
END LOOP;
END;
assoc_array_example.sql
Copyright 2011 Feuerstein and Associates Page 72
Oracle PL/SQL Programming
DECLARE
TYPE employees_aat IS TABLE OF employees%ROWTYPE
INDEX BY PLS_INTEGER;
l_employees employees_aat;
BEGIN
FOR employee_rec IN (SELECT * FROM employees)
LOOP
l_employees (l_employees.COUNT + 1) := employee_rec;
END LOOP;
END;
collection_of_records.sql
Copyright 2011 Feuerstein and Associates Page 73
Oracle PL/SQL Programming
aa_table_of_invalid_types.sql
indexby_options.sql
Nested Tables
CREATE OR REPLACE TYPE list_of_names_t IS TABLE OF NUMBER;
nested_table_example.sql
Copyright 2011 Feuerstein and Associates Page 78
Oracle PL/SQL Programming
Varrays
CREATE OR REPLACE TYPE list_of_names_t IS VARRAY (5) OF NUMBER;
varray_example.sql
Copyright 2011 Feuerstein and Associates Page 79
Oracle PL/SQL Programming
DECLARE Initialize in
TYPE numbers_t IS VARRAY (5) OF NUMBER;
salaries numbers_t := numbers_t (100, 200, 300);
declaration with
BEGIN values
DECLARE
TYPE numbers_t IS TABLE OF NUMBER; Initialize in
salaries numbers_t; execution
BEGIN section
salaries := numbers_t (100, 200, 300);
BEGIN
EXECUTE IMMEDIATE
'ALTER TYPE my_varray_t MODIFY LIMIT 100 CASCADE';
END;
/
varray_change_limit.sql
Copyright 2011 Feuerstein and Associates Page 83
Oracle PL/SQL Programming
collections_in_sql.sql
collections_in_sql_multiset.sql
Copyright 2011 Feuerstein and Associates Page 87
Oracle PL/SQL Programming
Non-Sequential Indexing
Sometimes you simply want to add items to the
end of a list.
This makes sense if the order in which items were
added is significant.
But how do you find a specific element in the list?
With sequential indexing, you have to scan through
the contents to find a match.
And what if you want to find elements in a
collection using more than one "index"?
Collections have just one index. No way around that.
string_tracker0.*
Copyright 2011 Feuerstein and Associates Page 88
Oracle PL/SQL Programming
emulate_primary_key1.sql
emulate_primary_key2.sql
Copyright 2011 Feuerstein and Associates Page 90
Oracle PL/SQL Programming
DECLARE
TYPE array_t1 IS TABLE OF NUMBER
INDEX BY BINARY_INTEGER;
TYPE array_t2 IS TABLE OF NUMBER
INDEX BY PLS_INTEGER;
TYPE array_t3 IS TABLE OF NUMBER
INDEX BY POSITIVE;
TYPE array_t4 IS TABLE OF NUMBER
INDEX BY NATURAL;
TYPE array_t5 IS TABLE OF NUMBER
INDEX BY VARCHAR2(64);
TYPE array_t6 IS TABLE OF NUMBER
INDEX BY VARCHAR2(32767);
TYPE array_t7 IS TABLE OF
INDEX BY NUMBER
employee.last_name%TYPE;
TYPE array_t8 IS TABLE OF NUMBER INDEX BY
types_pkg.subtype_t;
assoc_array*.sql
Copyright 2011 Feuerstein and Associates
assoc_array_perf.tst Page 94
Oracle PL/SQL Programming
string_tracker0.*
string_tracker1.*
Copyright 2011 Feuerstein and Associates Page 97
Oracle PL/SQL Programming
multdim*.*
Copyright 2011 Feuerstein and Associates Page 101
Oracle PL/SQL Programming
?
BEGIN
dept_in IN VARCHAR2);
salespkg.calc_total ('ABC');
PROCEDURE calc_total (
END;
dept_in IN CHAR);
END salespkg;
ambig_overloading.sql
Copyright 2011 Feuerstein and Associates Page 104
Oracle PL/SQL Programming
all_arguments.tst
all_arguments.sql
allargs.*
Copyright 2011 Feuerstein and Associates Page 105
Oracle PL/SQL Programming
String-based index
Is the 2nd
overloading of l_programs ('TOP_SALES') (2).EXISTS (0)
TOP_SALES a
function?
favorites_pkg.show_favorites (
'DISTINCT SET', keep_it_simple);
END;
authors.pkg
Copyright 2000-2008 Steven Feuerstein - Page 116 10g_set.sql
Copyright 2011 Feuerstein and Associates Page 116
Oracle PL/SQL Programming
in_clause.*
Copyright 2011 Feuerstein and Associates 10g_member_of.sql Page 117
Oracle PL/SQL Programming
authors.pkg
Copyright 2011 Feuerstein and Associates 10g_submultiset.sql Page 118
Oracle PL/SQL Programming
authors.pkg
10g_union.sql
Copyright 2011 Feuerstein and Associates Page 119
Oracle PL/SQL Programming
10g_intersect.sql
Copyright 2011 Feuerstein and Associates Page 120
Oracle PL/SQL Programming
10g_except.sql
Copyright 2011 Feuerstein and Associates Page 121
Oracle PL/SQL Programming
plsql_memory*.*
Copyright 2011 Feuerstein and Associates Page 124
Oracle PL/SQL Programming
string_tracker3.*
Copyright 2011 Feuerstein and Associates Page 126
Oracle PL/SQL Programming
global_temp_tab_vs_coll.sql
Row level
UPDATE row N
A Collection-Based Solution
Since you cannot perform the processing desired in the
row-level trigger, you need to defer the action until you
get to the statement level.
If you are going to defer the work, you have to remember
what you needed to do.
An associative array is an ideal repository for this reminder list.
Writes to list
1st row trigger fires
Work List
(collection)
Writes to list
Nth row trigger fires
Process data
mutating_trigger.pkg
in the list.
ranking.pkg Statement Trigger
Performance penalty
for many context
switches
Copyright 2011 Feuerstein and Associates Page 134
Oracle PL/SQL Programming
Update... Update...
Update... Update...
Update... Update...
Update... Update...
Update... Update...
Update... Fewer context switches, Update...
same SQL behavior
Copyright 2011 Feuerstein and Associates Page 136
Oracle PL/SQL Programming
statement_trigger_and_forall.sql
Copyright 2011 Feuerstein and Associates Page 137
Oracle PL/SQL Programming
bulklimit_stop.sql
Copyright 2011 Feuerstein and Associates Page 145
Oracle PL/SQL Programming
10g_optimize_cfl.sql
Copyright 2011 Feuerstein and Associates Page 146
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
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
bulk_rowcount.sql
Relational
Table
Phase 3: FORALL from collection to table
Copyright 2011 Feuerstein and Associates Page 160
Oracle PL/SQL Programming
cfl_to_bulk_0.sql
Copyright 2011 Feuerstein and Associates cfl_to_bulk_5.sql Page 161
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.
RETURN retval;
tabfunc_scalar.sql
END lotsa_names;
BEGIN
INSERT INTO tickertable
SELECT *
FROM TABLE (stockpivot (CURSOR (SELECT *
FROM stocktable)));
END;
/
tabfunc_setup.sql
Copyright 2011 Feuerstein and Associates
tabfunc_pipelined.sql Page 172
Oracle PL/SQL Programming
method_2_example.sql
updnval*.*
Copyright 2011 Feuerstein and Associates Page 181
Oracle PL/SQL Programming
PROCEDURE update_emps (
col_in IN VARCHAR2, empnos_in IN numList) IS
enames NameList;
BEGIN
FORALL indx IN empnos_in.FIRST .. empnos_in.LAST
EXECUTE IMMEDIATE
'UPDATE emp SET ' || col_in || ' = ' || col_in
|| ' * 1.1 WHERE empno = :1
RETURNING ename INTO :2'
USING empnos_in (indx )
RETURNING BULK COLLECT INTO enames; Notice that empnos_in is
... indexed, but enames is not.
END;
emp_cv sys_refcursor;
empnos numlist_t;
enames namelist_t;
l_employees employee_t;
BEGIN
OPEN emp_cv FOR 'SELECT empno, ename FROM emp_' || loc_in;
FETCH emp_cv BULK COLLECT INTO empnos, enames;
CLOSE emp_cv;
IF line_in = 2
it? THEN
process_line2;
END IF;
...
IF line_in = 22045
THEN
process_line22045;
END IF;
END;
Dynamic PL/SQL
Dynamically construct, compile and run an
anonymous block with EXECUTE IMMEDIATE.
Begins with BEGIN or DECLARE.
Ends with END;. The trailing semi-colon is required;
otherwise it is parsed as an SQL statement.
You can only reference globally-accessible data
structures (declared in a package specification).
Exceptions can (and should) be trapped in the
block from which the dynamic PL/SQL was
executed.
dynplsql*.sql
Copyright 2011 Feuerstein and Associates Page 187
Oracle PL/SQL Programming
exec_ddl_from_file.sql
desccols.pkg
desccols.tst
Copyright
Copyright 2011 2000-2008 Steven
Feuerstein and Feuerstein - Page 193
Associates Page 193
Oracle PL/SQL Programming
sqlcode.sql
sqlcode_test.sql
Copyright 2011 Feuerstein and Associates Page 198
Oracle PL/SQL Programming
SQLERRM Details
If you don't pass an argument to SQLERRM, it returns
the error message for the SQLCODE value.
When called outside of an exception handler, always
returns "success" message no error.
You can also pass an error code to SQLERRM and it
will return the generic error message.
The maximum size of a string returned by SQLERRM
is 512 bytes.
When there is a stack of errors, Oracle may truncate the
string returned by SQLERRM.
Oracle recommends you use
DBMS_UTILITY.FORMAT_ERROR_STACK instead.
sqlerrm.sql
DBMS_UTILITY.FORMAT_CALL_STACK
The "call stack" reveals the path taken through
your application code to get to that point.
Very useful whenever tracing or logging
errors.
The string is formatted to show line number
and program unit name.
But it does not reveal the names of subprograms
in packages.
callstack.sql
callstack.pkg
DBMS_UTILITY.FORMAT_ERROR_STACK
This built-in returns the error stack in the
current session.
Possibly more than one error in stack.
Returns NULL when there is no error.
Returns a string of maximum size 2000 bytes
(according to the documentation).
Oracle recommends you use this instead of
SQLERRM, to reduce the chance of truncation.
errorstack.sql
big_error_stack.sql
DBMS_UTILITY.FORMAT_ERROR_BACKTRACE
The backtrace function (new to 10.2) answers the
question: "Where was my error raised?
Prior to 10.2, you could not get this information from
within PL/SQL.
Call it whenever you are logging an error.
When you re-raise your exception (RAISE;) or raise a
different exception, subsequent BACKTRACE calls will
point to that line.
So before a re-raise, call BACKTRACE and store that
information to avoid losing the original line number.
backtrace.sql
bt.pkg
Copyright 2011 Feuerstein and Associates Page 203
Oracle PL/SQL Programming
errors_and_dml.sql
Copyright 2011 Feuerstein and Associates Page 205
Oracle PL/SQL Programming
dbms_errlog.sql
Copyright 2011 Feuerstein and Associates Page 208
Oracle PL/SQL Programming
UPDATE employees
SET salary = salary_in
LOG ERRORS REJECT LIMIT 100;
dbms_errlog_helper.sql
dbms_errlog_helper_demo.sql
Copyright 2011 Feuerstein and Associates Page 211
Oracle PL/SQL Programming
11g_emplu*.*
The SIMPLE_INTEGER
and real Native Compilation
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';
AFTER STATEMENT IS
BEGIN
mutating.sql ...
11g_compound_mutating.sql END AFTER STATEMENT;
END full_mfe_excuse_transaction;
Copyright 2011 Feuerstein and Associates Page 221
11g Oracle PL/SQL Programming
multiple_triggers.sql
trigger_conflict.sql
Copyright 2011 Feuerstein and Associates Page 222
11g Oracle PL/SQL Programming
11g_native_sequence.sql
Copyright 2011 Feuerstein and Associates Page 223
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.
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 228
11g Oracle PL/SQL Programming
plscope_unused_exceptions.sql
plscope_hierarchy.sql
plscope_naming_conventions.sql
plscope_helper_setup.sql
plscope_helper.pkg
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.
exec_ddl_from_file.sql
exec_ddl_from_file_11g.sql
Copyright 2011 Feuerstein and Associates Page 233
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
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.
11g_to_refcursor.sql
Copyright 2011 Feuerstein and Associates Page 235
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.
11g_to_cursorid.sql
Copyright 2011 Feuerstein and Associates Page 236
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.
11g_random_cursor_handle.sql
11g_access_denied_1.sql
Copyright 2011 Feuerstein and Associates 11g_effective_user_id.sql Page 237
11g Oracle PL/SQL Programming
11g_gen_invoc.sql
Hard-Coding Avoidance:
Principles and Concepts
Single point of definition (no repetition)
You should always aim for a single point of definition or
SPOD for everything in your application.
Information hiding the name is the thing
Avoid exposing the implementation details of formulas,
rules, algorithms, data access.
The more you hide, the more flexibility you have.
"Never" and "Always" in software
It's never going to stay the same.
It's always going to change.
Pay attention to that "voice in your head."
Copyright 2011 Feuerstein and Associates Page 247
Oracle PL/SQL Programming
Where's the hard-coding?
1 PROCEDURE process_employee (department_id_in IN NUMBER)
2 IS
3 l_id INTEGER; l_salary NUMBER (9,2); hardcoding.sql
4 l_name VARCHAR2 (100);
5
6 /* Full name: LAST COMMA FIRST (ReqDoc 123.A.47) */
7 CURSOR emps_in_dept_cur
8 IS
9 SELECT employee_id, salary, last_name || ',' || first_name lname
10 FROM employees
11 WHERE department_id = department_id_in;
12 BEGIN
13 OPEN emps_in_dept_cur;
14
15 LOOP
16 FETCH emps_in_dept_cur
17 INTO l_id, l_salary, l_name;
18
19 IF l_salary > 10000000 THEN adjust_comp_for_ceo (l_salary);
20 ELSE analyze_compensation (l_id, l_salary, 10000000); END IF;
21
22 EXIT WHEN emps_in_dept_cur%NOTFOUND;
23 END LOOP;
24 COMMIT;
25 EXCEPTION WHEN NO_DATA_FOUND THEN
26 RAISE_APPLICATION_ERROR (-20907, 'Invalid department ID');
27 END;
Copyright 2011 Feuerstein and Associates Page 248
Oracle PL/SQL Programming
soft_code_literals.sql
this. END;
RAISE;
Conclusions
Magic value hard-coding is the most
commonly recognized form of hard-coding.
It is also the easiest to remove from your
code.
Use constants or functions to hide the value,
define that value in one place.
"Single point of definition" or SPOD
fetch_into_record.sql
Copyright 2011 Feuerstein and Associates Page 260
Oracle PL/SQL Programming
no_more_hardcoding.sql
Copyright 2011 Feuerstein and Associates Page 263
Oracle PL/SQL Programming
SUBTYPEs
You can't always use %TYPE or %ROWTYPE in your
declaration.
You can, however, always define a "subtype" or
subset of an existing type with the SUBTYPE
statement. SUBTYPE benefits:
Avoid exposing and repeating constraints.
Give application-specific names to types. Critical when
working with complex structures like collections of
records, and nested collections.
Apply constraints, such as numeric ranges, to the variable
declared with the subtype.
Applying SUBTYPEs
Two key scenarios:
Whenever you are about to write a VARCHAR2(N)
or other constrained declaration, define a subtype
instead, preferably in a package specification.
Instead of writing a comment explaining a
declaration, put the explanation into a subtype.
DECLARE
Instead l_full_name VARCHAR2(100);
of this: l_big_string VARCHAR2(32767);
DECLARE
Write
l_full_name employees_rp.full_name_t;
this: l_big_string plsql_limits.maxvarchar2; fullname.pks
plsql_limits.pks
string_tracker3.*
Copyright 2011 Feuerstein and Associates Page 266
Oracle PL/SQL Programming
Conclusions
Declarations offer a danger of hard-coding of
both datatype and constraint on that type.
Assume that over time everything will change.
Apply the same "single point of definition"
principle to your declarations.
Use %TYPE and %ROWTYPE whenever possible.
Fall back on subtypes to define application specific
types and PL/SQL limits.
SQL as a Service
Think of SQL as a service that is provided to you, not
something you write.
Or if you write it, you put it somewhere so that it can be
easily found, reused, and maintained.
Conclusions
SQL statements are among the most critical
parts of your application.
You should have a clearly defined set of
guidelines about when, where and how to
write SQL.
Most important: Don't repeat SQL statements
(a form of hard-coding).
Logging Errors
We usually, but not always, want to write error
information out to a log table. How's this?
WHEN NO_DATA_FOUND
THEN
l_code := SQLCODE;
INSERT INTO errlog
VALUES ( l_code
, 'No company for id ' || TO_CHAR ( v_id )
, 'fixdebt', SYSDATE, USER );
WHEN OTHERS
THEN
l_code := SQLCODE; l_errm := SQLERRM;
INSERT INTO errlog
VALUES (l_code, l_errm, 'fixdebt', SYSDATE, USER );
RAISE;
END;
WHEN NO_DATA_FOUND
THEN
q$error_manager.register_error (
text_in => 'No company for id ' || TO_CHAR ( v_id ));
WHEN OTHERS
THEN
q$error_manager.raise_unanticipated (
name1_in => 'COMPANY_ID', value1_in => v_id);
END;
my_commit.perform_commit (
'DELETED ' || SQL%ROWCOUNT ||
' on iteration ' || cmtind);
END LOOP;
END;
While committing,
also pass trace
information. my_commit.*
Copyright
Copyright 2010 Steven
2011 Feuerstein Feuerstein - Page 294
and Associates Page 294
Oracle PL/SQL Programming
Your Reward
Elegant, functional code that you and others
can maintain easily
The respect of your peers
A deep sense of satisfaction with a job well
done
The opportunity to continue making a very
fine living, mostly from just thinking about
abstractions.
Extreme Modularization
Spaghetti code is the bane of
a programmer's existence.
It is impossible to understand
and therefore debug or
Organize your
maintain code that has long,
code so that the
twisted executable sections. executable
Fortunately, it is really easy to section has no
make spaghetti code a thing more than fifty
of the past. lines of code.
Explanation of Subprograms
Function calls_are_unhandled: takes no arguments,
returns TRUE if there is still at least one unhandled
call, FALSE otherwise.
Function current_caseload: returns the number of
calls (case load) assigned to that employee.
Function avg_caseload_for_dept: returns the average
number of calls assigned to employees in that
department.
Procedure assign_next_open_call: assigns the
employee to the call, making it handled, as opposed
to unhandled.
FUNCTION current_caseload (
employee_id_in IN employees.employee_id%TYPE) RETURN PLS_INTEGER
IS BEGIN ... END current_caseload;
FUNCTION avg_caseload_for_dept (
employee_id_in IN employees.employee_id%TYPE) RETURN PLS_INTEGER
IS BEGIN ... END current_caseload;
PROCEDURE assign_next_open_call (
employee_id_in IN employees.employee_id%TYPE)
IS BEGIN ... END assign_next_open_call;
BEGIN
Should I copy and paste? No! I should extract the program and
expand its scope.
PROCEDURE show_caseload (
department_id_in IN departments.department_id%TYPE)
IS BEGIN ... END show_caseload; distribute show_
_calls caseload
PROCEDURE distribute_calls (
department_id_in IN departments.department_id%TYPE
)
IS BEGIN ... END distribute_calls;
END;
current_
caseload
Now current_caseload is at the package
level and can be called by any program in
the package.
locmod_step_by_step.sql
Copyright 2011 Feuerstein and Associates Page 304
Oracle PL/SQL Programming
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