Friday, 15 February 2013

RETURNIG MULTIPLE VALUES INTO A SINGLE LINE FUNCTIONS

RETURNIG MULTIPLE VALUES INTO A SINGLE LINE FUNCTIONS:
---------------------------------------------------

One of the major drawbacks of inline SQL functions is the fact that they can return only one value. What do we do when we need to fetch more than one value? Calling the inline function more than once per row would be very expensive and would defeat the purpose -- or would it? Actually, by encapsulating the function into a package, we can exploit some package variables to call the function twice but execute it only once. For example:

SELECT E.emp_no, E.emp_name,
       SUM(S.days)    sick_days,
       COUNT(S.rowid) sick_occasions,
       SUM(H.days)    holidays,
       COUNT(H.rowid) holiday_occasions
FROM   holiday_leave H,
       sick_leave S,
       emp E
WHERE  E.emp_no = :emp_no
AND    E.emp_no = S.emp_no (+)
And    E.emp_no = H.emp_no (+)
                            ----- Sick ----   --- Holidays --
Result : Emp_No  Emp_Name   Days Occasions   Days Occasions
         123456  Tom Jones   12      8         80      8
This SQL statement is still incorrect, but translating it to inline functions is a bit more complex.

PACKAGE Sum_Emp
AS
  FUNCTION Sum_Sick_Leave (emp  IN number) RETURN number;
  FUNCTION Sum_Sick_Occasions (emp  IN number) RETURN number;
  FUNCTION Sum_Holiday_Leave (emp  IN number) RETURN number;
  FUNCTION Sum_Holiday_Occasions (emp  IN number) RETURN number;
  PRAGMA restrict_references (Sum_Sick_Leave,        WNDS, WNPS);
  PRAGMA restrict_references (Sum_Sick_Occasions,    WNDS, WNPS);
  PRAGMA restrict_references (Sum_Holiday_Leave,     WNDS, WNPS);
  PRAGMA restrict_references (Sum_Holiday_Occasions, WNDS, WNPS);
END;
PACKAGE BODY  Sum_Emp
 AS
   last_sick_emp      NUMBER;
   sick_days          NUMBER;
   sick_occasions     NUMBER;
   last_holiday_emp   NUMBER;
   holidays           NUMBER;
   holidays_occasions NUMBER;
   CURSOR C1 (emp IN number) IS
   SELECT SUM(days), COUNT(*)
   FROM   sick_leave
   WHERE  emp_no = emp;
   CURSOR C2 (emp IN number) IS
   SELECT SUM(days), COUNT(*)
   FROM   holiday_leave
   WHERE  emp_no = emp;
   FUNCTION Sum_Sick_Leave (emp  IN number) RETURN number
   AS
   BEGIN
     If NVL(Last_Sick_Emp,-1) != emp THEN
       OPEN  C1;
       FETCH C1 INTO sick_days, sick_occasions;
       CLOSE C1;
       Last_Sick_Emp := Emp;
    END IF;
    RETURN (sick_days);
  END;
  FUNCTION Sum_Sick_Occasions (emp  IN number) RETURN number
  AS
  BEGIN
    IF NVL(Last_Sick_Emp,-1) != emp THEN
      OPEN  C1;
      FETCH C1 INTO sick_days, sick_occasions;
      CLOSE C1;
      Last_Sick_Emp := Emp;
    END IF;
    RETURN (sick_occasions);
  END;
  FUNCTION Sum_Holiday_Leave (emp  IN number) RETURN number
  AS
  BEGIN
    IF NVL(Last_Holiday_Emp,-1) != emp THEN
      OPEN  C2;
      FETCH C2 INTO holidays, holiday_occasions;
      CLOSE C2;
      Last_Holiday_Emp := Emp;
    END IF;
    RETURN (holidays);
  END;
  FUNCTION Sum_Holiday_Occasions (emp  IN number) RETURN number
  AS
  BEGIN
    IF NVL(Last_Holiday_Emp,-1) != emp THEN
      OPEN  C2;
      FETCH C2 INTO holidays, holiday_occasions;
      CLOSE C2;
      Last_Holiday_Emp := Emp;
    END IF;
    RETURN (holiday_occasions);
  END;
END;
SELECT E.emp_no, E.emp_name,
       sum_emp.sum_sick_leave(E.emp_no)        sick_days,
       sum_emp.sum_sick_occasions(E.emp_no)    sick_occasions,
       sum_emp.sum_holiday_leave(E.emp_no)     holidays
       sum_emp.sum_holiday_occasions(E.emp_no) holiday_occasions
FROM   emp E
WHERE  E.emp_no = :emp_no
                            ----- Sick ----   --- Holidays --
Result : Emp_No  Emp_Name   Days Occasions   Days Occasions
         123456  Tom Jones    6      4         20      2
Note:  Packages that contain in-line SQL functions must define those functions as read-only via the RESTRICT_REFERENCES pragma. Inline functions can never perform update operations, and this pragma defines the function as read-only to the outside world. Packaged functions that occur in the SELECT list need pragma WNDS (described below). Functions that appear within the WHERE clause require WNPS.

PRAGMA RESTRICT_REFERENCE

PRAGMA RESTRICT_REFERENCE

When a stand alone user defined function is used in a SQL statement, there is no guarantee that the function does not update the database. This guarantee can be made by:

Inspecting the code, or by
Executing a SQL statement with the function. If the SQL parse identifies an INSERT, UPDATE, or DELETE, the execution will fail. You do not have to execute the specific path for the statement to fail. If the code contains ANY table modification SQL, you will know it.
If user defined functions are used to any extent it is advisable to encapsulate them within a package and designate this package as "pure", that is, the functions in the package do not modify the database. The "purity" of the package is denoted with the PL/SQL pragma: RESTRICT_REFERENCES. This pragma has one mandatory argument and three optional arguments. The optional arguments define the level of the "purity" of the function.

RESTRICT_REFERENCES Pragma Syntax
                                                     v-----|
PRAGMA RESTRICT_REFERENCES-----function_name--,----RNDS---)----><
                              |---DEFAULT ---|     WNDS
                                                   RNPS
                                                   WNPS
                                                   TRUST
Click on each parameter in BLUE for an explanation of its use ...

Usage Notes

You can declare the pragma RESTRICT_REFERENCES only in a package spec or object type spec.
You can specify up to four constraints (RNDS, RNPS, WNDS, WNPS) To call the function from parallel queries, you must specify all four constraints.
No constraint implies another. For example, WNPS does not imply RNPS.

When you specify TRUST, the function body is not checked for violations of the constraints listed in the pragma.
The function is trusted not to violate them.

If you specify DEFAULT instead of a function name, the pragma applies to all functions in the package spec or object type spec (including, in the latter case, the system-defined constructor).
You can still declare the pragma for individual functions. Such pragmas override the default pragma.

A RESTRICT_REFERENCES pragma can apply to only one function declaration. So, a pragma that references the name of overloaded functions always applies to the nearest foregoing function declaration.

The following code segment illustrates a package that provides procedures and functions in employee data and uses the pragma RESTRICT_REFERENCES.

create or replace package emp_pkg is
   function highest_paid_employee return emp.emp%name;
   -- returns the highest paid employee name
   function round_salary(n varchar2) return number;
   -- returns the ROUNDED salary of the employee
   pragma restrict_references(round_salary, WNDS);
end emp_pkg;

The following is a list of topics covered in this section.
Click on the item for a more in depth look.


Requirements for Calling Functions in SQL
Restrictions on User-Defined Functions in SQL
Replacing DECODEs with IF Statements
PRAGMA RESTRICT_REFERENCES



Requirements for Calling Functions in SQL
----------------------------------------------
There are several requirements that a programmer-defined PL/SQL function must meet in order to be callable from within a SQL statement:
The function must be stored in the database. A function defined in a client-side PL/SQL environment cannot be called from within SQL; there would be no way for SQL to resolve the reference to the function.

All of the function’s parameters must use the IN mode. Neither IN OUT nor OUT parameters are allowed in SQL-embedded stored functions—you should never have IN OUT and OUT parameters in functions. Whether or not you are going to use that function inside a SQL statement, such parameters constitute side effects of the main purpose of the function, which is to return a single value.

The datatypes of the function’s parameters, as well as the datatype of the RETURN clause of the function, must be recognized within the Oracle Server. While all of the Oracle Server datatypes are valid within PL/SQL, PL/SQL has added new datatypes that are not (yet) supported in the database. These datatypes include BOOLEAN, BINARY_INTEGER, associative arrays, PL/SQL records, and programmer-defined subtypes.

Prior to Oracle8i, functions defined in packages must have an associated RESTRICT_REFEFRENCES pragma defined for them. If you want to call from SQL a function defined in a package, you will need to add a pragma to the package specification asserting explicitly that this function is valid for SQL execution.
Note: By default, user-defined functions that execute in SQL operate on a single row of data, not on an entire column of data that crosses rows, as the group functions SUM, MIN, and AVG do. It is possible to write aggregate functions to be called inside SQL, but this requires taking advantage of the ODCIAggregate interface, which is part of Oracle’s Extensibility Framework.

Restrictions on User-Defined Functions in SQL
-------------------------------------------------
In order to guard against nasty side effects and unpredictable behavior, the Oracle RDBMS makes it impossible for your stored function in SQL to take any of the following actions:
The stored function may not modify database tables. It cannot execute an INSERT, DELETE, or UPDATE statement. Note that this restriction is relaxed if your function is defined as an autonomous transaction; in this case, any changes made in your function occur independently of the outer transaction in which the query was executed.

A stored function that is called remotely or through a parallelized action may not read or write the values of package variables. The Oracle Server does not support side effects that cross user sessions.

A stored function can update the values of package variables only if that function is called in a select list, or a VALUES or SET clause. If the stored function is called in a WHERE or GROUP BY clause, it cannot write package variables.

Prior to Oracle8, you cannot call RAISE_APPLICATION_ERROR from within the stored function.

The stored function may not call another module (stored procedure or function) that breaks any of the preceding rules. A function is only as pure as the most impure module that it calls.

The stored function may not reference a view that breaks any of the preceding rules. A view is a stored SELECT statement; that view’s SELECT may use stored functions.

Replacing DECODEs With IF Statements
----------------------------------
The DECODE function offers IF-like capabilities in the nonprocedural SQL environment provided by the Oracle Server. You can use the DECODE syntax to create matrix reports with a fixed number of columns or to perform complex IF-THENELSE logic within a query. The downside to DECODE is that it can be difficult to write and very difficult to maintain. Consider the following example of using DECODE to determine whether a date is within the prescribed range and, if so, to add to the count of rows that fulfill this requirement:

SELECT FC.year_number,
SUM (DECODE (GREATEST (ship_date, FC.q1_sdate),
 ship_date,
 DECODE (LEAST (ship_date, FC.q1_edate),
  ship_date, 1,
  0),
 0)) Q1_results,
SUM (DECODE (GREATEST (ship_date, FC.q2_sdate),
 ship_date,
 DECODE (LEAST (ship_date, FC.q2_edate),
  ship_date, 1,
  0),
 0)) Q2_results,
SUM (DECODE (GREATEST (ship_date, FC.q3_sdate),
 ship_date,
 DECODE (LEAST (ship_date, FC.q3_edate),
  ship_date, 1,
  0),
 0)) Q3_results,
SUM (DECODE (GREATEST (ship_date, FC.q4_sdate),
 ship_date,
 DECODE (LEAST (ship_date, FC.q4_edate),
  ship_date, 1,
  0),
 0)) Q4_results
 FROM orders O,
      fiscal_calendar FC
GROUP BY year_number;
The result set for this query might look like this:

YEAR NUMBER Q1 RESULTS Q2 RESULTS Q3 RESULTS Q4 RESULTS
------------ ---------- ---------- ---------- ----------
1993  12000  14005  22000  40000
1994  10000  15000  21000  55004
While it is very handy to use DECODE to produce such a report, the SQL required to accomplish the task is more than a little frightening. Try to interpret the Q1 RESULTS nested DECODE:

If the ship date is greater than or equal to the first quarter start date and less than or equal to the first quarter end date, then add one to the sum of the total number of orders shipped in that quarter. Otherwise, add zero.

Unfortunately, unless you are experienced in interpreting DECODE statements, you may find it difficult to glean this understanding from that convoluted SQL statement. The repetition in that single SELECT also cries out for modularization, which can be done with the following stored function (incr_in_range means “increment if in the range”):

FUNCTION incr_in_range
   (ship_date_in IN DATE, sdate_in IN DATE, edate_in IN DATE)
   RETURN INTEGER
IS
BEGIN
   IF ship_date_in BETWEEN sdate_in AND edate_in
   THEN
      RETURN 1;
   ELSE
      RETURN 0;
   END IF;
END;
With the incr_in_range function, that long SELECT statement simply becomes:

SELECT FC.year_number,
       SUM (incr_in_range (ship_date, q1_sdate, q1_edate)) Q1_results,
       SUM (incr_in_range (ship_date, q2_sdate, q2_edate)) Q2_results,
       SUM (incr_in_range (ship_date, q3_sdate, q3_edate)) Q3_results,
       SUM (incr_in_range (ship_date, q4_sdate, q4_edate)) Q4_results
  FROM orders O,
       fiscal_calendar FC
 GROUP BY year_number;
This stored function gets rid of the code redundancy and makes the SELECT statement much more readable. In addition, this function could be used in other SQL statements to perform the same logic.

The PRAGMA RESTRICT_REFERENCES (Oracle8 and earlier)
------------------------------------------------------
Prior to Oracle8i, if you wanted to invoke within SQL a function that was defined inside a package specification, you would have had to provide a RESTRICT_REFERENCES pragma (a compiler directive or instruction) for that function. This pragma asserts the “purity level” of the function, in essence promising Oracle that the function has the specified side effects (or, more to the point, lack thereof).
Working with the RESTRICT_REFERENCES pragma can be very frustrating, so it was a great relief to many a PL/SQL developer when in Oracle8i this pragma was made unnecessary. However, this section talks briefly about some of the rules associated with this pragma for those still using Oracle8 and earlier.

You need a separate PRAGMA statement for each packaged function you wish to use in a SQL statement, and it must come after the function declaration in the package specification. To assert a purity level with the pragma, use the following syntax:

PRAGMA RESTRICT_REFERENCES
 (function_name, WNDS [, WNPS] [, RNDS] [, RNPS])
where function_name is the name of the function whose purity level you wish to assert, and the four codes have the following meanings:

WNDS - Writes No Database State. Asserts that the function does not modify any database tables.
WNPS - Writes No Package State. Asserts that the function does not modify any package variables.
RNDS - Reads No Database State. Asserts that the function does not read any database tables.
RNPS - Reads No Package State. Asserts that the function does not read any package variables.
Here is an example of two different purity-level assertions for functions in the company_financials package:
PACKAGE company_financials
IS
   FUNCTION company_type (type_code_in IN VARCHAR2)
      RETURN VARCHAR2;
   FUNCTION company_name (
      company_id_in IN company.company_id%TYPE)
      RETURN VARCHAR2;
   PRAGMA RESTRICT_REFERENCES (
       company_type, WNDS, RNDS, WNPS, RNPS);
   PRAGMA RESTRICT_REFERENCES (company_name, WNDS, WNPS, RNPS);
END company_financials;
In this package, the company_name function reads from the database to obtain the name for the specified company. Notice that both pragmas were placed together at the bottom of the package specification; the pragma does not need to immediately follow the function specification. Also the WNPS and RNPS arguments were specified for both functions. Oracle recommends that you assert the highest possible purity levels so that the compiler will never reject the function unnecessarily.

Note: If a function you want to call in SQL calls a procedure or function in a package, you must also provide a RESTRICT_REFERENCES pragma for that program. You cannot call a procedure directly in SQL, but if it is going to be executed indirectly from within SQL, it still must follow the rules.

If your function violates its pragma, you will receive the PLS-00452 error: subprogram ‘program’ violates its associated pragma. Suppose, for example, that the body of the company_financials package looks like this:

CREATE OR REPLACE PACKAGE BODY company_financials
IS
   FUNCTION company_type (type_code_in IN VARCHAR2)
      RETURN VARCHAR2
   IS
      v_sal NUMBER;
   BEGIN
      SELECT sal INTO v_sal FROM emp WHERE empno = 1;
      RETURN 'bigone';
   END;
   FUNCTION company_name (company_id_in IN company.company_id%TYPE)
      RETURN VARCHAR2
   IS
   BEGIN
      UPDATE emp SET sal = 0;
      RETURN 'bigone';
   END;
END company_financials;
When you attempt to compile this package body you will get the following error:

3/4 PLS-00452: Subprogram 'COMPANY_TYPE' violates its associated pragma
because the company_type function reads from the database and the RNDS purity level has been asserted. If you remove the SELECT statement, you will then receive this error:

11/4 PLS-00452: Subprogram 'COMPANY_NAME' violates its associated pragma
because the company_name function updates the database and you have asserted the WNDS level. You will sometimes look at your function and say, “Hey, I absolutely do not violate my purity level. There is no UPDATE, DELETE, or UPDATE around.” Maybe not. But there is a good chance that you are calling a built-in package or in some other way breaking the rules. Again, if you are running Oracle8i and above, you no longer need to deal with RESTRICT_REFERENCES. The runtime engine will automatically check your code for any violations.

PRAGMA

PRAGMA:
----------------
The PRAGMA keyword is used to signify that the remainder of the PL/SQL statement is a pragma or directive to the compiler.
Pragmas are processed at compile time; they do not execute during run-time.
A pragma is a special instruction to the compiler.
Also called a pseudoinstruction, the pragma doesn't change the meaning of a program. It simply passes information to the compiler.
It is very similar, in fact, to the tuning hints you can embed in a SQL statement inside a block comment.

PL/SQL offers the following pragmas.
        These are the     EXCEPTION_INIT,
                RESTRICT_REFERENCES,
and (new with Oracle8i)     AUTONOMOUS TRANSACTION and
                SERIALLY_REUSABLE pragma.

AUTONOMOUS_TRANSACTION Pragma

AUTONOMOUS_TRANSACTION Pragma V8.1-10.2
--------------------------------------

Before the release of PL/SQL 8.1, each Oracle session could have at most one active transaction at a given time.
In other words,any and all changes made in your session had to be either saved or erased in their entirety.
This restriction has long been considered a drawback in the PL/SQL world.
Developers have requested the ability to execute and save or cancel certain DML statements (INSERT, UPDATE, DELETE) without affecting the overall session’s transaction.
You can now accomplish this goal with the autonomous transaction feature of PL/SQL 8.1 and above.
When you define a PL/SQL block (anonymous block, procedure, function, packaged procedure, packaged function, database trigger) as an autonomous transaction, you isolate the DML in that block from the caller’s transaction context.
That block becomes an independent transaction that is started by another transaction, referred to as the main transaction.

Within the autonomous transaction block, the main transaction is suspended.
You perform your SQL operations, commit or roll back those operations, and resume the main transaction.

To define a PL/SQL block as an autonomous transaction you simply include the following statement in your declaration section:

PRAGMA AUTONOMOUS_TRANSACTION;

The pragma instructs the PL/SQL compiler to establish a PL/SQL block as autonomous or independent.
For the purposes of the autonomous transaction, a PL/SQL block can be any of the following:

Top-level (but not nested) anonymous PL/SQL blocks
Functions and procedures, defined either in a package or as standalone programs
Methods (functions and procedures) of a SQL object type
Database triggers

You can put the autonomous transaction pragma anywhere in the declaration section of your PL/SQL block.
The best option, however, would be to place it before any data structure declarations.
That way, anyone reading your code will immediately identify the program as an autonomous transaction.
This pragma is the only syntax change made to PL/SQL to support autonomous transactions.
COMMIT, ROLLBACK, the DML statements—all the rest is as it was before. However, these statements have a different scope of impact and visibility when executed within an autonomous transaction,
and you will have to include a COMMIT or ROLLBACK in your program.


The AUTONOMOUS_TRANSACTION pragma instructs the PL/SQL compiler to mark a routine as autonomous (independent).
An autonomous transaction is an independent transaction started by another transaction, the main transaction.
Autonomous transactions let you suspend the main transaction, do SQL operations, commit or roll back those operations, then resume the main transaction.

You cannot use the pragma to mark all subprograms in a package (or all methods in an object type) as autonomous. Only individual routines can be marked autonomous.
You can code the pragma anywhere in the declarative section of a routine. But, for readability, code the pragma at the top of the section.

Once started, an autonomous transaction is fully independent. It shares no locks, resources, or commit-dependencies with the main transaction.
So, you can log events, increment retry counters, and so on, even if the main transaction rolls back.

Unlike regular triggers, autonomous triggers can contain transaction control statements such as COMMIT and ROLLBACK.
Also, unlike regular triggers, autonomous triggers can execute DDL statements (such as CREATE and DROP) using native dynamic SQL.

Changes made by an autonomous transaction become visible to other transactions when the autonomous transaction commits.
The changes also become visible to the main transaction when it resumes, but only if its isolation level is set to READ COMMITTED (the default).

If you set the isolation level of the main transaction to SERIALIZABLE,
as follows, changes made by its autonomous transactions are not visible to the main transaction when it resumes:

When in the main transaction, rolling back to a savepoint marked before you started an autonomous transaction does not roll back the autonomous transaction.
Remember, autonomous transactions are fully independent of the main transaction.

If an autonomous transaction attempts to access a resource held by the main transaction (which cannot resume until the autonomous routine exits), a deadlock can occur.
In that case, Oracle raises an exception in the autonomous transaction, which is rolled back if the exception goes unhandled.

If you try to exit an active autonomous transaction without committing or rolling back,
Oracle raises an exception. If the exception goes unhandled, the transaction is rolled back.

PRAGMA AUTONOMOUS_TRANSACTION-Data base triggers
-------------------------------------------------
The benefit of autonomous transactions for database triggers is that inside those triggers you can now issue COMMITs and ROLLBACKs, statements that are otherwise not allowed in database triggers.
The changes you commit and roll back will not affect the main transaction that caused the database trigger to fire.
They will only apply to DML activity taking place inside the trigger itself (or through stored program units called within the trigger).
You may want to take an action in the database trigger that is not affected by the ultimate disposition of the transaction that caused the trigger to fire.
For example, suppose that you want to keep track of each action against a table, whether or not the action is completed. You might even want to be able to detect which actions failed. You can use autonomous transactions to do this.

First, construct a simple autonomous transaction trigger on the ceo_compensation table that writes a simple message to the following ceo_comp_history table. Here are the two table definitions:

CREATE TABLE ceo_compensation (
   company VARCHAR2(100),
   name VARCHAR2(100),
   compensation NUMBER,
   layoffs NUMBER);
CREATE TABLE ceo_comp_history (
   name VARCHAR2(100),
   description VARCHAR2(255),
   occurred_on DATE);
Here is the before-insert trigger to run all the elements in the script:

CREATE OR REPLACE TRIGGER bef_ins_ceo_comp
BEFORE INSERT ON ceo_compensation FOR EACH ROW
DECLARE
   PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
   INSERT INTO ceo_comp_history VALUES (
      :new.name, 'BEFORE INSERT', SYSDATE);
   COMMIT;
END;
/
With this trigger in place, every insert attempt can be tracked, as shown in the steps below:

BEGIN
   INSERT INTO ceo_compensation VALUES (
      'Mattel', 'Jill Barad', 9100000, 2700);
     
   INSERT INTO ceo_compensation VALUES (
      'American Express Company',
      'Harvey Golub', 33200000, 3300);
     
   INSERT INTO ceo_compensation VALUES (
      'Eastman Kodak', 'George Fisher', 10700000, 20100);
     
   ROLLBACK; --I wish!
END;
/
SELECT name,
       description,
       TO_CHAR (occurred_on,
          'MM/DD/YYYY HH:MI:SS') occurred_on
  FROM ceo_comp_history;  
NAME                 DESCRIPTION           OCCURRED_ON
-------------------- --------------------- -------------------
Jill Barad           BEFORE INSERT         03/17/1999 04:00:56
Harvey Golub         BEFORE INSERT         03/17/1999 04:00:56
George Fisher        BEFORE INSERT         03/17/1999 04:00:56

Fine-Tuning the Database Trigger
----------------------------------
But there is something of a problem with the trigger just defined.
Tthe trigger was defined as an autonomous transaction because the alert was performed in the body of the trigger.
Suppose you want to perform some additional DML for the main transaction here in the trigger.
It won’t be rolled back with the rest of the transaction (if a rollback occurs).
Generally, it is recommended that you not make a database trigger itself the autonomous transaction.
Instead, push all of the independent DML activity (such as writing to the audit or history table) into its own procedure.
 Make that procedure the autonomous transaction. Have the trigger call the procedure.

First, create the audit procedure:

CREATE OR REPLACE PROCEDURE audit_ceo_comp (
   name IN VARCHAR2,
   description IN VARCHAR2,
   occurred_on IN DATE
   )
IS
   PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
   INSERT INTO ceo_comp_history VALUES (
      audit_ceo_comp.name,
      audit_ceo_comp.description,
      audit_ceo_comp.occurred_on
      );
   COMMIT;
END;
/
Then change the trigger to the following.

CREATE OR REPLACE TRIGGER aft_ins_ceo_comp
AFTER INSERT ON ceo_compensation FOR EACH ROW
DECLARE
   ok BOOLEAN := is_valid_comp_info (:NEW.name);
BEGIN
   IF ok
   THEN
      audit_ceo_comp (
         :new.name, 'AFTER INSERT', SYSDATE);
   ELSE
      RAISE VALUE_ERROR;
   END IF;
END;
/
Note the following differences:
------------------------------------
The trigger is now an after-insert trigger, rather than a before-insert trigger.
Wait until after the INSERT to the compensation table takes place, then perform the audit.
When the is_valid_comp_info function returns FALSE, do not even perform an audit.
Instead, stop the transaction by raising an error.
This demonstrates the other reason you don’t want the trigger itself to be autonomous.
In some situations, you will always want to perform an audit.
Under other circumstances, however, you may want to stop the main transaction by raising an exception.
Both of those events can't happen if the exception is raised in the same block and transaction as the audit DML.


As you take advantage of the new autonomous transaction pragma, plan out how you will be using these new code elements.
You will almost always be better off hiding the details of your new, independent transactions behind a procedural interface.

AUTONOMOUS TRANSACTION USES:

You will want to define your program module as an autonomous transaction whenever you want to isolate the changes made in that module from the caller’s transaction context.
Here are some specific ideas where autonomous transactions are useful:

Logging mechanism
----------------
This is the classic example of the need for an autonomous transaction. You need to log error information in a database table, but don't want that log entry to be a part of the logical transaction.

Commits and rollbacks in your database triggers
--------------------------------------------------
If you define a trigger as an autonomous transaction, then you can commit and/or roll back in that code.

Retry counter
--------------------
Autonomous transactions can help you keep track of how many times a user tries to connect to a database or get access to a resource (you'll reject access after a certain number of attempts).

Software usage meter
------------------------
You want to keep track of how often a program is called during an application session.
 This information is not dependent on, and cannot affect, the transaction being processed in the application.

Reusable application components
------------------------------------
It is becoming ever more important to be able to offer standalone units of work (also known as cartridges) that get their job done without any side effects on the calling environment.
Autonomous transactions will play a crucial role in this area.

PRAMA LOGGING MECHANISM:
--------------------------

A very common requirement in applications is to keep a log of errors that occur during transaction processing.
The most convenient repository for this log is a database table; with a table, all the information is retained in the database and you can use SQL to retrieve and analyze the log.
One problem with a database table log, however, is that entries in the log become a part of your transaction.
If you perform (or have performed to you) a ROLLBACK, you can easily erase your log.
You can use savepoints to preserve your log entries while cleaning up your transaction, but that approach is complicated.
With autonomous transactions, however, logging becomes simpler, more manageable, and less error prone.

Suppose a log table is defined as follows:

CREATE TABLE log81tab (
    code INTEGER,
    text VARCHAR2(4000),
    created_on DATE,
    created_by VARCHAR2(100),
    changed_on DATE,
    changed_by VARCHAR2(100),
    machine VARCHAR2(100),
    program VARCHAR2(100)
    );
It can be used to store errors (SQLCODE and SQLERRM) that have occurred, or even for non-error-related logging.
The machine and program columns record information available from the virtual V$ SESSION table, as you will see.

Never expose your underlying logging mechanism by explicitly inserting into it in your exception sections and other locations.
Instead, you should build a layer of code around the table (this is known as encapsulation).
There are two reasons why you should do this:

If you ever change your table’s structure, all those uses of the log table will not be disrupted.
People will be able to use the log table in a much easier, more consistent manner.
So here is a simple logging package. It consists of two procedures:

CREATE OR REPLACE PACKAGE log81
IS
   PROCEDURE putline (
      code_in IN INTEGER,
      text_in IN VARCHAR2
      );
     
   PROCEDURE saveline (
      code_in IN INTEGER,
      text_in IN VARCHAR2
      );
END;
/
The log81.saveline procedure (as you will see in the package body) is an autonomous transaction routine,
whereas log81.putline simply performs the insert. Here is the package body:

CREATE OR REPLACE PACKAGE BODY log81
IS
   CURSOR sess IS
       SELECT MACHINE, PROGRAM
         FROM V$SESSION
        WHERE AUDSID = USERENV('SESSIONID');
   rec sess%ROWTYPE;
   PROCEDURE putline (
      code_in IN INTEGER,
      text_in IN VARCHAR2
      )
   IS
   BEGIN
      INSERT INTO log81tab
           VALUES (
              code_in,
              text_in,
              SYSDATE,
              USER,
              SYSDATE,
              USER,
              rec.machine,
              rec.program
           );
   END;
  
   PROCEDURE saveline (
      code_in IN INTEGER,
      text_in IN VARCHAR2
      )
   IS
      PRAGMA AUTONOMOUS_TRANSACTION;
   BEGIN
      putline (code_in, text_in);
      COMMIT;
   EXCEPTION WHEN OTHERS THEN ROLLBACK;
   END;
BEGIN
   OPEN sess; FETCH sess INTO rec; CLOSE sess;
END;
/
Here are some comments on this implementation that you might find helpful:

Some useful information from V$SESSION is obtained when the package is initialized (the values are not going to change during your session, so you should only query it once)
and that information should be incorporated into the log.
The putline procedure performs the straight insert. You would probably want to add some exception handling to this program if you applied this idea in your production application.
The saveline procedure calls the putline procedure, but does so from within the context of an autonomous transaction.
With this package in place, the error handler shown earlier can be as simple as this:

EXCEPTION
   WHEN OTHERS
   THEN
      log81.saveline (SQLCODE, SQLERRM);
END;
Developers don’t have to concern themselves with the structure of the log table. They don’t even have to know they are writing to a database table. Because an autonomous transaction was used,
they can rest assured that no matter what happens in their application, the log entry has been saved.

PRAGMA BUILDING A RETRY COUNTER:
---------------------------------

Suppose that you want to let a user try to get access to a resource (a file, a row of data, etc.) N times before an outright rejection.
You also want to keep track of attempts between connections to the database. The autonomous transaction is a perfect fit, due to the COMMITs required.
A retry mechanism allows you to specify the “item” on which you are placing a limit and keeping track of attempts.
These limits are maintained for each unique username. Here is the specification of this package:

CREATE OR REPLACE PACKAGE retry
IS      
   PROCEDURE incr_attempts (item IN VARCHAR2);
  
   PROCEDURE set_limit (item IN VARCHAR2, limit IN INTEGER);
  
   FUNCTION limit (item IN VARCHAR2) RETURN INTEGER;
   FUNCTION limit_reached (item IN VARCHAR2) RETURN BOOLEAN;
      
   PROCEDURE clear_attempts (item IN VARCHAR2);
  
   FUNCTION attempts (item IN VARCHAR2) RETURN INTEGER;
      
   FUNCTION attempts_left (item IN VARCHAR2) RETURN INTEGER;
      
   FUNCTION attempted_at (item IN VARCHAR2) RETURN DATE;
      
   PROCEDURE show_retries (item IN VARCHAR2 := '%');
END retry;
/
The programs are self-explanatory; the implementations are also very straightforward.
Here, for example, is the implementation of the procedure that lets you increment the number of attempts.
Notice the COMMITs and ROLLBACKs; these are required since the autonomous transaction pragma has been used.

PROCEDURE incr_attempts (item IN VARCHAR2)
IS
   PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
   INSERT INTO retry_counter VALUES (
      USER, incr_attempts.item, SYSDATE, 1);
   COMMIT;
EXCEPTION
   WHEN DUP_VAL_ON_INDEX
   THEN
      UPDATE retry_counter
         SET last_attempt = SYSDATE,
             tries = tries + 1
       WHERE username = USER
         AND item = incr_attempts.item;
      COMMIT;
        
   WHEN OTHERS THEN ROLLBACK; RAISE;
END;

PRAGMA AUTONOUMOUS TRANSACTION RULES AND RESTRICIONS:

While it is certainly very easy to add the autonomous transaction pragma to your code,
there are some rules and restrictions on the use of this feature.
You can only make a top-level anonymous block an autonomous transaction. The following construction will work:

DECLARE
   PRAGMA AUTONOMOUS_TRANSACTION;
   myempno NUMBER;
BEGIN
   INSERT INTO emp VALUES (myempno, ...);
   COMMIT;
END;
/
whereas this construction:

DECLARE
   myempno NUMBER;
BEGIN
   DECLARE
      PRAGMA AUTONOMOUS_TRANSACTION;
   BEGIN 
      INSERT INTO emp VALUES (myempno, ...);
      COMMIT;
   END;
END;
/
results in this error:

PLS-00710: PRAGMA AUTONOMOUS_TRANSACTION cannot be declared here
You can now use COMMIT and ROLLBACK inside your database triggers. These actions will not affect the transaction that caused the database trigger to fire.

If an autonomous transaction attempts to access a resource held by the main transaction (which has been suspended until the autonomous routine exits), you can cause a deadlock to occur in your program. Here is a simple example to demonstrate the problem. A procedure to perform an update is created, and then called after already having all rows updated:

CREATE OR REPLACE PROCEDURE
   update_salary (dept_in IN NUMBER)
IS
   PRAGMA AUTONOMOUS_TRANSACTION;
  
   CURSOR myemps IS
      SELECT empno FROM emp
       WHERE deptno = dept_in
         FOR UPDATE NOWAIT;
BEGIN
   FOR rec IN myemps
   LOOP
      UPDATE emp SET sal = sal * 2
       WHERE empno = rec.empno;
   END LOOP;
   COMMIT;
END;
/
  
BEGIN
   UPDATE emp SET sal = sal * 2;
   update_salary (10);
END;
/
This results in the following error:

ERROR at line 1:
ORA-00054: resource busy and acquire with NOWAIT specified
You cannot mark all subprograms in a package (or all methods in an object type) as autonomous with a single PRAGMA declaration. You must indicate autonomous transactions explicitly in each program. For example, the following package specification is invalid:

CREATE PACKAGE warcrimes_pkg
AS
   PRAGMA AUTONOMOUS_TRANSACTION;
  
   PROCEDURE register (
      culprit IN VARCHAR2, event IN VARCHAR2);
END warcrimes_pkg;
/
One consequence of this rule is that you cannot tell by looking at the package specification which, if any, programs will run as autonomous transactions.

To exit without errors from an autonomous transaction program, you must perform an explicit commit or rollback. If the program (or any program called by it) has transactions pending, the runtime engine will raise an exception—and then it will roll back those uncommitted transactions. Suppose, for example, that your job is to take over failing companies and make them profitable by firing employees. You would then want to use this procedure:

CREATE OR REPLACE PROCEDURE fire_em_all
IS
   PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
   DELETE FROM emp;
END;
/
You want to make the program an autonomous transaction because you don’t want anyone to back out the changes when you are not looking. If you do not explicitly commit, when you run this procedure, the following error occurs:

SQL> exec fire_em_all
*
ERROR at line 1
ORA-06519: active autonomous transaction detected and rolled back
The COMMIT and ROLLBACK statements end the active autonomous transaction; they do not, however, force the termination of the autonomous routine. You can have multiple COMMIT and/or ROLLBACK statements inside your autonomous block.

An autonomous block is one in which autonomous transactions are expected. Zero, one, or more autonomous transactions can be executed within an autonomous block.

You can roll back only to savepoints marked in the current transaction. When you are in an autonomous transaction you cannot roll back to a savepoint set in the main transaction. If you try to do so, the runtime engine will raise this exception:

ORA-01086: savepoint 'your savepoint' never established
The TRANSACTIONS parameter in the Oracle initialization file (INIT.ORA) specifies the maximum number of transactions allowed concurrently in a session. If you use autonomous transactions (which run concurrently with the main transaction), you might exceed this number—and raise an exception—unless you raise the TRANSACTIONS value. This is the error you will get if you encounter this problem:

ORA-01574: maximum number of concurrent transactions exceeded
The default value for TRANSACTIONS in Oracle8i is 75.

Using Autonomous Transactions from Within SQL
Ever since Oracle 7.3, you have been able to call your own functions from within SQL—provided that you follow a variety of rules. The main one is this: you are not allowed to update the database. And you can’t save or cancel changes from within the function.
With the autonomous transaction feature, however, the picture changes. An autonomous transaction program never violates the two database-related purity levels, RNDS (reads no database state) and WNDS (writes no database state), even if that program actually does read from or write to the database. This is because those purity levels or constraints apply to the SQL statement (which, in this case, is the main transaction), yet an autonomous transaction’s DML actions never affect the main transaction.

As long as you define a program to be an autonomous transaction, you can also call it directly or indirectly in a SQL statement. If your program cannot assert another purity level, such as WNPS (writes no package state), you may be restricted from calling that program in certain parts of the SQL statement, such as the WHERE clause.

As an example, suppose that you want to keep a trace of all the rows that have been touched by a query. This table is created:

CREATE TABLE query_trace (
   table_name VARCHAR2(30),
   rowid_info ROWID,
   queried_by VARCHAR2(30),
   queried_at DATE
   );
This function is then created to perform the audit:

CREATE OR REPLACE FUNCTION traceit (
   tab IN VARCHAR2,
   rowid_in IN ROWID)
   RETURN INTEGER
IS
BEGIN
   INSERT INTO query_trace VALUES (tab, rowid_in, USER, SYSDATE);
   RETURN 0;
END;
/
When you try to use this function inside a query, you get the following error:

SQL> select ename, traceit ('emp', rowid) from emp;
                   *
ERROR at line 1:
ORA-14551: cannot perform a DML operation inside a query
However, if traceit is now transformed into an autonomous transaction by adding the pragma (and committing the results before the RETURN statement), the results are very different. The query works, and the query_trace table is filled:

SQL> SELECT ename, traceit ('emp', ROWID)
  2    FROM emp;
ENAME      TRACEIT('EMP',ROWID)
---------- --------------------
KING                          0
...
MILLER                        0
14 rows selected.
SQL>
SQL> SELECT table_name, rowid_info, queried_by,
  2         TO_CHAR (queried_at, 'HH:MI:SS') queried_at
  3    FROM query_trace;
TABLE_NAME ROWID_INFO         QUERIED_BY QUERIED_AT
---------- ------------------ ---------- ----------
emp        AAADEPAACAAAAg0AAA SCOTT      05:32:54
...
emp        AAADEPAACAAAAg0AAN SCOTT      05:36:50
You have other options when it comes to tracing queries: you can write to the screen with the DBMS_OUTPUT built-in package or send information to a pipe with DBMS_PIPE. Now that autonomous transactions are available, if you do want to send information to a database table (or delete rows or update data, etc.), you can take that route instead, but be sure to analyze carefully the overhead of this approach.

Transaction Visibility
The default behavior of autonomous transactions is that once a COMMIT or ROLLBACK occurs in the autonomous transaction,
those changes are visible immediately in the main transaction.
Oracle offers a SET TRANSACTION statement option to hide those changes from the main transaction:

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
The default isolation level is READ COMMITTED, which means that as soon as changes are committed,
 they are visible to the main transaction.

As is usually the case with the SET TRANSACTION statement, you must call it before you initiate your transactions
(i.e., issue any SQL statements); in addition, the setting affects your entire session, not just the current program.

The following script demonstrates the SERIALIZABLE isolation level at work.

First, an autonomous transaction procedure is created:

CREATE OR REPLACE PROCEDURE fire_em_all
IS
   PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
   DELETE FROM emp2;
   COMMIT;
END;
/
A script is run that sets the isolation level to SERIALIZABLE,
then the number of rows that appear in the emp2 table at the following times are displayed:

Before I call fire_em_all
After I call fire_em_all but before the main transaction is committed or rolled back
After I commit in the main transaction, here is the script that is run:
DECLARE
   PROCEDURE showcount (str VARCHAR2) IS
      num INTEGER;
   BEGIN
      SELECT COUNT(*) INTO num FROM emp2;
      DBMS_OUTPUT.PUT_LINE (str || ' ' || num);
   END;
BEGIN
   SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
   showcount ('Before isolated AT delete');
   fire_em_all;
   showcount ('After isolated AT delete');
   COMMIT;
   showcount ('After MT commit');
END;
/
Here is the output from running the script:

Before isolated AT delete 14
After isolated AT delete 14
After MT commit 0

EXCEPTION_INIT pragma

EXCEPTION_INIT pragma:
----------------------------
The EXCEPTION_INIT pragma is used to tell the compiler to associate a particular error number with an exception declared in your program. You should only associate a single error code to a specific exception.

This allows specification of a handler to take care of any exceptions that result from an Oracle error.
If you don't specify the EXCEPTION_INIT clause, you must allow for processing of non-specified exceptions in the OTHERS exception area.

EXCEPTION_INIT Pragma Format
PRAGMA EXCEPTION_INIT(exception_name,error_number)
Click on each parameter in BLUE for an explanation of its use ...

The EXCEPTION_INIT clause must appear in the same declarative section as its associated exception handler.

DECLARE
   exception_name EXCEPTION;
   PRAGMA EXCEPTION_INIT (exception_name, error_code_literal);
BEGIN
where exception_name is the name of an exception and error_code_literal is the number of the Oracle error (including the minus sign, if the error code is negative, as is almost always the case).

In the following program code, we declare and associate an exception for this error:

ORA-2292 violated integrity constraining (OWNER.CONSTRAINT) -
         child record found.
This error occurs if we try to delete a parent record while there are child records still in that table.
A child record is a record with a foreign key reference to the parent table:

PROCEDURE delete_company (company_id_in IN NUMBER)
IS
   /* Declare the exception. */
   still_have_employees EXCEPTION;
   /* Associate the exception name with an error number. */
   PRAGMA EXCEPTION_INIT (still_have_employees, -2292);
BEGIN
   /* Try to delete the company. */
   DELETE FROM company
    WHERE company_id = company_id_in;
EXCEPTION
   /* If child records were found, this exception is raised! */
   WHEN still_have_employees
   THEN
      DBMS_OUTPUT.PUT_LINE
         (' Please delete employees for company first.');
END;
When you use EXCEPTION_INIT, you must supply a literal number for the second argument of the pragma call.
By explicitly naming this system exception, the purpose of the exception handler is self-evident.

The EXCEPTION_INIT pragma improves the readability of your programs by assigning names to otherwise obscure error numbers.
You can employ the EXCEPTION_INIT pragma more than once in your program. You can even assign more than one exception name to the same error number.

CLIENT SERVER ERROR COMMUNICATION
--------------------------------------


A conventional approach with procedural constraint enforcement in the client/server arena is to call RAISE_APPLICATION_ERROR from within a trigger. This is done if a procedural constraint is violated, for example, the salaries in the EMP table are all increased to a point where the department sum exceeds some threshold. This error can be caught by the client. In the WebServer environment, this error is written to the log file of the DCD. The end result is the user gets a generic error message on the Web page and they have no clue as to what the problem is.
When developing applications for the WebServer, you must rely more heavily on pragma exception_init. In this case, let’s assume that a stored procedure performs an INSERT that fires a trigger that may raise an application error using the error number -20100. In this case the stored procedure should contain the following:

PROCEDURE process_form(...) IS
   invalid_update EXCEPTION;
   PRAGMA EXCEPTION_INIT(invalid_update, -20100);
BEGIN
   INSERT INTO emp VALUES (...);
EXCEPTION
   WHEN invalid_update THEN
      ..do something so the user gets a new page
      ..with a header message indicating the failure.
 END;

Oracle provides the RAISE_APPLICATION_ERROR procedure to communicate application-specific errors from the server side (usually a database trigger) to the client-side application. This built-in procedure is the only mechanism available for communicating a server-side, programmer-defined exception to the client side in such a way that the client process can handle the exception.
Here is an example of a call to this built-in:

RAISE_APPLICATION_ERROR (-20001, 'Invalid Registration');
When you call RAISE_APPLICATION_ERROR, it is as though an exception has been raised with the RAISE statement. Execution of the current PL/SQL block halts immediately, and all the effects of the program are rolled back. The built-in returns a programmer-defined error number and message back to the client component of the application. You can then use the EXCEPTION_INIT pragma and exception handlers to handle the error in a graceful, user-friendly manner.

The error number you specify must be between –20000 and –20999 so you do not conflict with any Oracle error numbers.

The error message can be up to 2K bytes in length; if it is longer, it will not abort the call to RAISE_APPLICATION_ERROR; the procedure will simply truncate anything beyond the 2K.

The exception handler architecture, combined with RAISE_APPLICATION_ERROR and the On-Error trigger, allows your front end application to rely on business rules embedded in the database to perform validation and communicate problems to the user. When you make use of RAISE_APPLICATION_ERROR, however, it is entirely up to you to manage the error numbers and messages. To help manage your error codes and provide a consistent interface with which developers can handle server errors, you might consider building a package.

RAISE_APPLICATION_ERROR in a Database Trigger
Suppose you need to implement a database trigger which stops records from being inserted into the database if the person is less than 18 years old. The code for this trigger would look like this:

CREATE OR REPLACE TRIGGER minimum_age_check
BEFORE INSERT ON employee
FOR EACH ROW
BEGIN
   IF ADD_MONTHS (:new.birth_date, 18*12) > SYSDATE
   THEN
      RAISE_APPLICATION_ERROR
         (-20001, 'Employees must at least eighteen years of age.');
   END IF;
END;
On the client side, a program can be written like the following to detect and handle this exception:

DECLARE
   /* Declare the exception. */
   no_babies_allowed EXCEPTION;
   /* Associate the name with the error number used in the trigger. */
   PRAGMA EXCEPTION_INIT (no_babies_allowed, -20001);
BEGIN
   /* Attempt to insert the employee. */
   INSERT INTO employee ... ;
EXCEPTION
   /* Handle the server-side exception. */
   WHEN no_babies_allowed
   THEN
      /*
      || SQLERRM will return the message passed into the
      || RAISE_APPLICATION_ERROR built-in.
      */
      DBMS_OUTPUT.PUT_LINE (SQLERRM);
END;

PRAGMA SERIALLY_REUSABLE

PRAGMA SERIALLY_REUSABLE
--------------------------------
Prior to PL/SQL V8, any data declared in a package simply stayed around until the end of the session, whether or not it was needed any more by the application. While this is an important feature of PL/SQL packages (persistent, global data), it limits scalability since such memory grows linearly with the number of users.
To help applications better manage memory usage, PL/SQL V8 provided the pragma SERIALLY_REUSABLE, which lets you mark some packages as "serially reusable." You can so mark a package if its state is needed only for the duration of a call to the server (for example, an OCI call to the server, a PL/SQL client-to-server, or server-to-server RPC).

The global memory for such packages is not kept in the memory area per user, but instead in a small SGA pool for reuse. Before reuse, the package global variables are initialized to NULL or to the default values provided.

The pool is kept in SGA memory so that the work area of a package can be reused across users who have requests for the same package. In this scheme, the maximum number of work areas needed for a package is only as many as there are concurrent users of the package, which is typically much fewer than the total number of logged on users. The user of "serially reusable" packages does increase the shared-pool requirements slightly, but this is more than offset by the decrease in the per-user memory requirements. Further, Oracle ages out work areas not in use when it needs to reclaim shared pool memory.

Note: Use this feature with care. Many of your existing packages may absolutely rely on the persistent data feature.

SERIALLY_REUSABLE Pragma Format:
PRAGMA SERIALLY_REUSABLE
The keyword pragma signifies that the statement is a pragma (compiler directive). Pragmas are processed at compile time, not at run time. They do not affect the meaning of a program; they simply convey information to the compiler.

Example
The following example shows how global variables in a "serially reusable" package behave across call boundaries:
CREATE OR REPLACE PACKAGE sr_pkg
IS
   PRAGMA SERIALLY_REUSABLE;
   num NUMBER := 0;
   PROCEDURE print_pkg;
   PROCEDURE init_pkg (n NUMBER);
END sr_pkg;
/
CREATE OR REPLACE PACKAGE BODY sr_pkg
IS
   -- the body is required to have the pragma since the
   -- specification of this package has the pragma
   PRAGMA SERIALLY_REUSABLE;
   -- Print package state
   PROCEDURE print_pkg IS
   BEGIN
      DBMS_OUTPUT.PUT_LINE ('num: ' || sr_pkg.num);
   END;
   -- Initialize package state
   PROCEDURE init_pkg (n NUMBER) IS
   BEGIN
      sr_pkg.num := n;
   END;
END sr_pkg;
/
Now we will exercise this package. First, we enable output from SQL*Plus:

SQLPLUS> set serveroutput on;
Next, we initialize the package with a value of 4 and then display the package contents -- all within a single PL/SQL block:

SQLPLUS> begin
        -- initialize and print the package
        SR_PKG.init_pkg(4);
        -- Print it in the same call to the server.
        -- We should see the new values.
        SR_PKG.print_pkg;
        end;
        /
Statement processed.
num: 4
And we see that initial value of 4. If we had placed the call to SR_PKG.print_pkg inside the same PL/SQL block, however, that package variable would lose its setting, as is shown in the following steps:

SQLPLUS> begin
        -- We should see that the package state is reset to the
        -- initial (default) values.
        SR_PKG.print_pkg;
        end;
        /
Statement processed.
num: 0

Thursday, 14 February 2013

PL/SQL – SQL TUNING


PL/SQL – SQL TUNING

INDEX
            An index is an ordered list of contents of a column, or a group of columns of a table.
  • Duplicate Index: Indexes that allow duplicate values for the indexed columns.
  • Unique Index: Indexed that deny duplicate values for the indexed columns.
  • Simple Index: Index created on a single column of a table.
  • Composite Index: Index created on more than one column of a table.
  • Reverse Key Indexes: Reverses each byte of the column being indexed while keeping the column order. Helps in avoiding performance degradation in Indexes where modifications to the index are concentrated on a small set of blocks. A Reverse Key Index can be built in to a normal index using the keywords REBUILD NOREVERSE.
ALTER INDEX <indexname> REBUILD NOREVERSE;
  • Bitmap Indexes: Advantages of BI are greatest for low cardinality columns i.e. columns in which the number of distinct values is small compared to the number of rows in the table.
  • Function Based Index: A function based index cannot be created on a LOB column, REF, nested table column or the object type contains a LOB, REF, or nested table.
  • Key Compressed Index: Key compression breaks an index key into a prefix and a suffix entry. Compression is achieved by sharing the prefix entries among all the suffix entries in an index block. Key Compression can be useful when in a non-unique index the ROWID is appended to make the key unique.
  • Partitioning Indexes: Similar to partitioning tables. Like table partitions, index partition could be in different Tablespaces. Partitioned Indexes are more complicated than partitioned tables.

CLUSTERS
            The concept of Cluster is where members records are stored PHYSICALLY near parent records. Clusters are used to store data from different tables in the same physical data blocks. They are appropriate to use if the records from those tables are frequently queried together.
  • Cluster Key: The columns within the cluster index are called the Cluster Key. (I.e. the set of columns that the tables in the cluster have in common).

SEQUENCES
            Sequence is an Oracle Object that generates numeric values. Value generated can have maximum of 38 digits. Generate is ASC or DESC order, provides intervals between numbers.

SNAPSHOTS
            Is a recent copy of a table from database or in some cases, a subset of rows/columns of a table. A snapshot is created on the destination system with the create snapshot SQL command. The remote table is immediately defined and populated from the master table.

HINTS
            The optimizer in Oracle Engine uses hints as suggestions for choosing an execution plan for the statement. A statement block can have only one  comment containing hints and that comment must follow the SELECT, UPDATE, INSERT and DELETE keyword.
{SELECT|UPDATE|INSERT|DELETE} /*+ hint [text]  [hint [text]] ….. */




Hints by functional category
Optimization Goals and Approaches
ALL_ROWS and FIRST_ROWS
CHOOSE
RULE
Join Order Hints
ORDERED
STAR
Access Methods Hints
AND EQUAL
CLUSTER
FULL
HASH
INDEX and NO_INDEX
INDEX_ASC and INDEX_DESC
INDEX_COMBINE
INDEX_FFS
ROWID
Query Transformation Hints
EXPAND_GSET_TO_UNION
FACT and NOFACT
MERGE
NO_MERGE
NO_EXPAND
REWRITE and NOREWRITE
STAR_TRANSFORMATION
USE_CONCAT
Join Operation Hints
DRIVING_SITE
HASH_SJ, MERGE_SJ and NL_SJ
LEADING
USE_HASH and USE_MERGE
USE_NL
Parallel Execution Hints
PARALLEL and NOPARALLEL
PARALLEL_INDEX
PQ_DISTRIBUTE
NOPARALLE_INDEX
Other Hints
      APPEND and NOAPPEND      CACHE and NOCACHE                            CURSOR_SHARING_EXACT                         
      DYNAMIC_SAMPLING          NESTED_TABLE_GET_REFS                   UNNEST and NO_UNNEST
      ORDERED_PREDICATES       PUSH_PRED and NO_PUSH_PRED       PUSH_SUBQ and NO_PUSH_SUBQ

Hints can be any of the optimizer mode values (CHOOSE, RULE, FIRST_ROWS, ALL_ROWS) or they can be one of the access paths mentioned above.

Access Path                                        Description
Basic Access Path Hints
  1. ROWID                        -           Uses ROWID scan for retrieval
  2. CLUSTER                     -           Uses a cluster key scan
  3. hash                            -           Uses a hash index scan
  4. INDEX_ASC                 -           Uses an index scan and scan in ascending order
  5. index_desc                  -           Uses an index scan and scans in descending order
  6. AND_EQuAL                -           Uses multiple indexes and merges their results
7.ordered                          -           Uses the order of the tables in the FROM clause to  be the order of the join.
8.use_nl                            -           Uses the nested loops method for joining tables
9.use_merge                     -           Uses the sort-merge method for joining tables
10.                                                  FULL    -           Uses a full table Scan

Additional Access Path for Hints (Version 7.3 and Later)
  1. CACHE                         -           Tells Oracle to treat the table as a cached table,    keeping its blocks in the SGA after a full scan for later quick access.
  2. HASH_AJ                     -           Specifies type of join to use during an antijoin.
  3. MERGE_AJ                  -           Specifies type of join to use during an antijoin.
  4. NO_CACHE                  -           Marks blocks as “least recently used” so they get removed from SGA soon.
  5. NONPARALLEL            -           Allows disabling parallelism of a query.
  6. ROWID                        -           Uses TABLE ACCESS BY ROWID operation
  7. STAR                            -           Uses a composite key/start query execution path when resolving a join.
  8. USE_CONCAT              -           Forces OR conditions in the WHERE clause to be compounded as UNION ALL.
  9. USE_HASH                  -           Uses a hash join









CONCURRENCY CONTROL IN ORACLE

The technique employed by the Oracle engine to protect table data when several people are accessing it is called Concurrency control. Oracle uses method called Locking to implement concurrency control when multiple users access a table to manipulate its data at the same time.

LOCKS
            Locks are mechanisms used to ensure data integrity while allowing maximum concurrent access to data. Oracle’s locking is fully automatic and requires no user intervention. The Oracle engine automatically locks table data while executing SQL statements. This type of locking is called Implicit Locking.

Types of Locks           
The type of lock to be placed on a resource depends on the operation being performed on that resource. Operations on tables can be distinctly grouped into the following two categories.
-       Read Operation: SELECT statements
-       Write Operation: INSERT, UPDATE, DELETE statements.

Two types of Locks supported by Oracle are:
  • Shared Locks: are placed on resources whenever a READ operation (SELECT) is performed. Multiple shared Locks can be simultaneously set on a resource.
  • Exclusive Locks: are placed on resources whenever Write operations (INSERT,UPDATE and DELETE) are performed. Only one exclusive lock can be placed on a resource at a time i.e. the first user who acquires an exclusive lock will continue to have the sole ownership of the resource, and no other user can acquire an exclusive lokc on that resource.

LEVELS OF LOCKS
            Three levels of locking:    Row Level, Page Level, Table Level

Releasing the Locks
            Locks are released under the following circumstances:
  • The transaction is committed successfully using the Commit verb
  • A rollback is performed
  • A rollback to a savepoint will release locks set after the specified savepoint.

Deadlock
            A deadlock occurs when two users have a lock, each on a separate object, and, they want to acquire a lock on each other’s object. When this happens, the first user has to wait for the second user to release the lock, but the second user will not release it until the lock on the first user’s object is freed. At this point, both the users are at an impasse and cannot proceed with their business. In such a case, Oracle detects the deadlock automatically and solves the problem by aborting one of the two transactions.
ERROR HANDLING IN PL/SQL
            When an SQL statement fails, the Oracle Engine is the first to recognize this as an Exception condition. The Oracle engine immediately tries to handle the exception condition and resolve it. This is done by raising a built-in Exception Handler. An exception handler is nothing but a code block in memory that will attempt to resolve the current exception condition.        
                                                            ORACLE’S NAMED EXCEPTION HANDLERS
DUP_VAL_ON_INDEX
Raised when an insert or update attempts to create two rows with duplicate values in column/s constrained by a unique index.
TIMEOUT_ON_RESOURCE
Raised when Oracle has been waiting to access a resource beyond the user-defined timeout limit.
LOGIN_DENIED
Raised when an invalid username/password was used to log onto oracle.
NOT_LOGGED_ON
Raised when PL/SQL issues an Oracle call without being logged onto Oracle.
NO_DATA_FOUND
Raised when a select statement returns zero rows.
PROGRAM_ERROR
Raised when PL/SQL has an internal problem.
TOO_MANY_ROWS
Raised when a select statement returns more than one row
VALUE_ERROR
Raised when the data type or data size is invalid.
OTHERS
Stands for all other exceptions not explicitly named.