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 can’t 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