Declaring an Autonomous Function in a Package
Example 6-43 Declaring an Autonomous Function in a Package
CREATE OR REPLACE PACKAGE emp_actions AS -- package specification
FUNCTION raise_salary (emp_id NUMBER, sal_raise NUMBER) RETURN NUMBER;
END emp_actions;
/
CREATE OR REPLACE PACKAGE BODY emp_actions AS -- package body
-- code for function raise_salary
FUNCTION raise_salary (emp_id NUMBER, sal_raise NUMBER) RETURN NUMBER IS
PRAGMA AUTONOMOUS_TRANSACTION;
new_sal NUMBER(8,2);
BEGIN
UPDATE employees SET salary = salary + sal_raise WHERE employee_id = emp_id;
COMMIT;
SELECT salary INTO new_sal FROM employees WHERE employee_id = emp_id;
RETURN new_sal;
END raise_salary;
END emp_actions;
/
Create GLOBAL TEMPORARY Table.
CREATE GLOBAL TEMPORARY TABLE admin_work_area
(startdate DATE,
enddate DATE,
class CHAR(20))
ON COMMIT DELETE ROWS;
Posted by Asif at 8:30 PM
CREATE TRIGGER Statement
Using Triggers:Example 9-1 CREATE TRIGGER Statement
CREATE OR REPLACE TRIGGER Print_salary_changes
BEFORE DELETE OR INSERT OR UPDATE ON emp
FOR EACH ROW
WHEN (NEW.EMPNO > 0)
DECLARE
sal_diff number;
BEGIN
sal_diff := :NEW.SAL - :OLD.SAL;
dbms_output.put('Old salary: ' || :OLD.sal);
dbms_output.put(' New salary: ' || :NEW.sal);
dbms_output.put_line(' Difference ' || sal_diff);
END;
/
CREATE TRIGGER -Calling a Procedure in a Trigger Body:
CREATE TRIGGER: Calling a Procedure in a Trigger Body: Example You could create
the salary_check trigger described in the preceding example by calling a procedure instead of providing
the trigger body in a PL/SQL block. Assume you have defined a procedure check_sal in the hr schema,
which verifies that an employee's salary is in an appropriate range. Then you could create the
trigger salary_check as follows:
CREATE TRIGGER salary_check
BEFORE INSERT OR UPDATE OF salary, job_id ON employees
FOR EACH ROW
WHEN (new.job_id <> 'AD_VP')
CALL check_sal(:new.job_id, :new.salary, :new.last_name)
Posted by Asif at 4:34 AM
Hierarchical Level Query Example using SYS_CONNECT_BY_PATH
SYS_CONNECT_BY_PATH: is valid only in hierarchical queries. It returns the path of a column value
from root to node, with column values separated by char for each row returned
by CONNECT BY condition.
Both column and char can be any of the datatypes CHAR, VARCHAR2, NCHAR, or NVARCHAR2.
The string returned is of VARCHAR2 datatype and is in the same character set as column.
Examples
The following example returns the path of employee names from employee Kochhar to
all employees of Kochhar (and their employees):
SELECT LPAD(' ', 2*level-1)||SYS_CONNECT_BY_PATH(last_name, '/') "Path"
FROM employees
START WITH last_name = 'Kochhar'
CONNECT BY PRIOR employee_id = manager_id;
Path
---------------------------------------------------------------
/Kochhar
/Kochhar/Greenberg
/Kochhar/Greenberg/Faviet
/Kochhar/Greenberg/Chen
/Kochhar/Greenberg/Sciarra
/Kochhar/Greenberg/Urman
/Kochhar/Greenberg/Popp
/Kochhar/Whalen
/Kochhar/Mavris
/Kochhar/Baer
/Kochhar/Higgins
/Kochhar/Higgins/Gietz
Hierarchical Queries -CONNECT_BY_ROOT Examples
Hierarchical Queries: CONNECT_BY_ROOT Examples The following example returns the last name
of each employee in department 110, each manager above that employee in the hierarchy, the number
of levels between manager and employee, and the path between the two:
SELECT last_name "Employee", CONNECT_BY_ROOT last_name "Manager",
LEVEL-1 "Pathlen", SYS_CONNECT_BY_PATH(last_name, '/') "Path"
FROM employees
WHERE LEVEL > 1 and department_id = 110
CONNECT BY PRIOR employee_id = manager_id;
Employee Manager Pathlen Path
--------------- ------------ ---------- -----------------------------------
Higgins Kochhar 1 /Kochhar/Higgins
Gietz Kochhar 2 /Kochhar/Higgins/Gietz
Gietz Higgins 1 /Higgins/Gietz
Higgins King 2 /King/Kochhar/Higgins
Gietz King 3 /King/Kochhar/Higgins/Gietz
Avoiding Collection Exceptions
Avoiding Collection Exceptions
In most cases, if you reference a nonexistent collection element, PL/SQL raises a predefined
exception. Consider the following example:
DECLARE
TYPE NumList IS TABLE OF NUMBER;
nums NumList; -- atomically null
BEGIN
/* Assume execution continues despite the raised exceptions. */
nums(1) := 1; -- raises COLLECTION_IS_NULL (1)
nums := NumList(1,2); -- initialize table
nums(NULL) := 3 -- raises VALUE_ERROR (2)
nums(0) := 3; -- raises SUBSCRIPT_OUTSIDE_LIMIT (3)
nums(3) := 3; -- raises SUBSCRIPT_BEYOND_COUNT (4)
nums.DELETE(1); -- delete element 1
IF nums(1) = 1 THEN ... -- raises NO_DATA_FOUND (5)
In the first case, the nested table is atomically null. In the second case, the subscript is null. In
the third case, the subscript is outside the legal range. In the fourth case, the subscript exceeds
the number of elements in the table. In the fifth case, the subscript designates a deleted
element.
The following list shows when a given exception is raised:
Collection Exception Raised when...
COLLECTION_IS_NULL
you try to operate on an atomically null collection.
NO_DATA_FOUND
a subscript designates an element that was deleted, or a nonexistent element of an associative
array.
SUBSCRIPT_BEYOND_COUNT
a subscript exceeds the number of elements in a collection.
SUBSCRIPT_OUTSIDE_LIMIT
a subscript is outside the allowed range.
VALUE_ERROR
a subscript is null or not convertible to the key type. This exception might occur if the key is
defined as a PLS_INTEGER range, and the subscript is outside this range.
In some cases, you can pass invalid subscripts to a method without raising an exception. For
instance, when you pass a null subscript to procedure DELETE, it does nothing. Also, you can
replace deleted elements without raising NO_DATA_FOUND, as the following example shows:
DECLARE
TYPE NumList IS TABLE OF NUMBER;
nums NumList := NumList(10,20,30); -- initialize table
BEGIN
nums.DELETE(-1); -- does not raise SUBSCRIPT_OUTSIDE_LIMIT
nums.DELETE(3); -- delete 3rd element
dbms_output.put_line(nums.COUNT); -- prints 2
nums(3) := 30; -- allowed; does not raise NO_DATA_FOUND
dbms_output.put_line(nums.COUNT); -- prints 3
END;
Posted by Asif at 1:28 AM
ORACLE-BASE - Using Ref Cursors To Return Recordsets
Using Ref Cursors To Return Recordsets
The Reference Cursor which is also called the Cursor Variables is available since Oracle 7.3 the REF
CURSOR type has been available to allow recordsets to be returned from stored procedures and functions.
Oracle 9i introduced the predefined SYS_REFCURSOR type, meaning we no longer have to define our
own REF CURSOR types. The example below uses a ref cursor to return a subset of the records in
the EMP table.
The following procedure opens a query using a SYS_REFCURSOR output parameter. Notice the cursor is
not closed in the procedure. It is up to the calling code to manage the cursor once it has been opened.
CREATE OR REPLACE
PROCEDURE get_emp_rs (p_deptno IN emp.deptno%TYPE,
p_recordset OUT SYS_REFCURSOR) AS
BEGIN
OPEN p_recordset FOR
SELECT ename,
empno,
deptno
FROM emp
WHERE deptno = p_deptno
ORDER BY ename;
END GetEmpRS;
/
The resulting cursor can be referenced from PL/SQL as follows.
SET SERVEROUTPUT ON SIZE 1000000
DECLARE
l_cursor SYS_REFCURSOR;
l_ename emp.ename%TYPE;
l_empno emp.empno%TYPE;
l_deptno emp.deptno%TYPE;
BEGIN
get_emp_rs (p_deptno => 30,
p_recordset => l_cursor);
LOOP
FETCH l_cursor
INTO l_ename, l_empno, l_deptno;
EXIT WHEN l_cursor%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(l_ename || ' | ' || l_empno || ' | ' || l_deptno);
END LOOP;
CLOSE l_cursor;
END;
/
Posted by
Oracle PL/SQL - Raise User-Defined Exception With Custom SQLERRM -
Stack Overflow
User defined Exception with Naming and assigning the Error Code no.
1 declare
2 ex_custom EXCEPTION;
3 PRAGMA EXCEPTION_INIT( ex_custom, -20001 );
4 begin
5 raise_application_error( -20001, 'This is a custom error' );
6 exception
7 when ex_custom
8 then
9 dbms_output.put_line( sqlerrm );
10* end;
SQL> /
ORA-20001: This is a custom error
PL/SQL procedure successfully completed.
Compound Triggers , Avoid Mutating-Table Error
Using Compound Triggers to Avoid Mutating-Table Error
You can use compound triggers to avoid the mutating-table error (ORA-04091) described in Trigger
Restrictions on Mutating Tables.
Scenario: A business rule states that an employee's salary increase must not exceed 10% of the
average salary for the employee's department. This rule must be enforced by a trigger.
Solution: Define a compound trigger on updates of the table hr.employees, as in Example 9-4. The
state variables are initialized each time the trigger fires (even when the triggering statement is
interrupted and restarted).
Example 9-4 Compound Trigger that Avoids Mutating-Table Error
CREATE OR REPLACE TRIGGER Check_Employee_Salary_Raise
FOR UPDATE OF Salary ON Employees
COMPOUND TRIGGER
Ten_Percent CONSTANT NUMBER := 0.1;
TYPE Salaries_t IS TABLE OF Employees.Salary%TYPE;
Avg_Salaries Salaries_t;
TYPE Department_IDs_t IS TABLE OF Employees.Department_ID%TYPE;
Department_IDs Department_IDs_t;
TYPE Department_Salaries_t IS TABLE OF Employees.Salary%TYPE
INDEX BY VARCHAR2(80);
Department_Avg_Salaries Department_Salaries_t;
BEFORE STATEMENT IS
BEGIN
SELECT AVG(e.Salary), NVL(e.Department_ID, -1)
BULK COLLECT INTO Avg_Salaries, Department_IDs
FROM Employees e
GROUP BY e.Department_ID;
FOR j IN 1..Department_IDs.COUNT() LOOP
Department_Avg_Salaries(Department_IDs(j)) := Avg_Salaries(j);
END LOOP;
END BEFORE STATEMENT;
AFTER EACH ROW IS
BEGIN
IF :NEW.Salary - :Old.Salary >
Ten_Percent*Department_Avg_Salaries(:NEW.Department_ID)
THEN
Raise_Application_Error(-20000, 'Raise too big');
END IF;
END AFTER EACH ROW;
END Check_Employee_Salary_Raise;
Mutating Oracle triggers
Avoiding Mutating triggers
The insertto the childtable causedthe foreignkeytovalidate the dataonthe parent (whichfiredthe
trigger) causingthe insertof the childtable toresultina mutatingtable erroron the parenttable.
The Oracle mutatingtriggererroroccurs whena triggerreferencesthe table thatownsthe trigger,
resultinginthe "ORA-04091: table name ismutating,trigger/functionmaynotsee it."message.
 Don't use triggers - The bestway to avoidthe mutatingtable errorisnot to use triggers. While
the object-orientedOracle provides"methods"thatare associatedwithtables,mostsavvyPL/SQL
developersavoidtriggersunlessabsolutelynecessary.
 Use an "after" or "insteadof" trigger- If youmust use a trigger,it'sbestto avoidthe mutating
table errorby usingan "after"trigger,toavoidthe currency issuesassociatedwithamutatingtable. For
example,usingatrigger":afterupdate onxxx",the original update hascompletedandthe table will not
be mutating.
 Re-work the trigger syntax - Dr. Hall has some greatnoteson mutatingtable errors,andoffers
otherwaysto avoidmutatingtableswith acombinationof row-levelandstatement-leveltriggers.
 Use autonomous transactions - You can avoidthe mutatingtable errorby markingyourtrigger
as an autonomoustransaction,makingitindependentfromthe table thatcallsthe procedure.
At the endof the day,the mutatingtable erroris usuallythe resultof apoor applicationdesignand
mutatingtriggersshouldbe avoidedwheneverpossible.
Steve Callanhasthese notesonthe ORA-04091 error:
"Here isa problemmanydevelopersruninto:ORA-04091 table owner.table_name ismutating,
trigger/functionmaynotsee it.In manycases,the cause of thiserror isdue to code withina triggerthat
looksat or touchesthe data withinthe table the trigger isbeingcalledorinvokedfrom.
The reasonOracle raisesthiserror isrelatedtoone of Oracle'sprimarystrengthsasa relational
database managementsystem.The particularstrengthinquestionhere isthatof havinga read
consistentview of data.
It isworthwhile tonote thatthisORA-04091 error occurs notonlyin the "pure"database development
environment,butalsointhe Oracle toolstype of developmentenvironmentsuchasOracle SQL*Forms.
AnothersolutionreliesonusinganINSTEAD-OFtriggerinsteadof the triggeryoumeanttouse when
youreceivedthe error.Anothersolutionisactuallymore of apreventative measure,namely,usingthe
righttype of triggerforthe taskat hand.
Perhapsthe greateststrengthorutilityof anINSTEAD-OFtriggerisitsabilitytoupdate whatwould
normallyappeartobe non-updateableviews.Simpleviews(prettymuchbasedonasingle base table)
generallyare inherentlyupdateableviaDML statementsissuedagainstthe view.
However,whenaviewbecomesmore complex (multiple tablesorviewsusedinvariousjoinconditions
to create the newsingle view),there isagoodchance that many columns,asreferencedbythe view,
lose their"updateable-ness."
So,beingthe data dictionaryview/tablename triviawizardthat youare,youknow to querythe
XXX_UPDATABLE_COLUMNSviews,substitutingUSER,ALL or DBA for XXXas applicable.
There are exceptionstothisrule aboutviewsbeinginherentlyupdateable.The exceptions(or
restrictions) include viewsthatuse aggregate functions;groupfunctions;use of the DISTINCTkeyword;
use of GROUP BY, CONNECTBY or STARTWITH clauses;anduse of some joins.Inmanycases,use of the
INSTEAD-OFtriggerfeature allowsyoutoworkaroundthese restrictions.
INSTEAD-OFtriggersare alsouseful forFormsdevelopersbecause formsare commonlybasedonviews.
The INSTEAD-OFtrigger,beinga"real"trigger,andnot a true form trigger,isstoredonthe server."

Trig

  • 1.
    Declaring an AutonomousFunction in a Package Example 6-43 Declaring an Autonomous Function in a Package CREATE OR REPLACE PACKAGE emp_actions AS -- package specification FUNCTION raise_salary (emp_id NUMBER, sal_raise NUMBER) RETURN NUMBER; END emp_actions; / CREATE OR REPLACE PACKAGE BODY emp_actions AS -- package body -- code for function raise_salary FUNCTION raise_salary (emp_id NUMBER, sal_raise NUMBER) RETURN NUMBER IS PRAGMA AUTONOMOUS_TRANSACTION; new_sal NUMBER(8,2); BEGIN UPDATE employees SET salary = salary + sal_raise WHERE employee_id = emp_id; COMMIT; SELECT salary INTO new_sal FROM employees WHERE employee_id = emp_id; RETURN new_sal; END raise_salary; END emp_actions; / Create GLOBAL TEMPORARY Table. CREATE GLOBAL TEMPORARY TABLE admin_work_area (startdate DATE, enddate DATE, class CHAR(20)) ON COMMIT DELETE ROWS; Posted by Asif at 8:30 PM CREATE TRIGGER Statement Using Triggers:Example 9-1 CREATE TRIGGER Statement CREATE OR REPLACE TRIGGER Print_salary_changes BEFORE DELETE OR INSERT OR UPDATE ON emp FOR EACH ROW WHEN (NEW.EMPNO > 0) DECLARE sal_diff number; BEGIN sal_diff := :NEW.SAL - :OLD.SAL; dbms_output.put('Old salary: ' || :OLD.sal); dbms_output.put(' New salary: ' || :NEW.sal); dbms_output.put_line(' Difference ' || sal_diff); END; /
  • 2.
    CREATE TRIGGER -Callinga Procedure in a Trigger Body: CREATE TRIGGER: Calling a Procedure in a Trigger Body: Example You could create the salary_check trigger described in the preceding example by calling a procedure instead of providing the trigger body in a PL/SQL block. Assume you have defined a procedure check_sal in the hr schema, which verifies that an employee's salary is in an appropriate range. Then you could create the trigger salary_check as follows: CREATE TRIGGER salary_check BEFORE INSERT OR UPDATE OF salary, job_id ON employees FOR EACH ROW WHEN (new.job_id <> 'AD_VP') CALL check_sal(:new.job_id, :new.salary, :new.last_name) Posted by Asif at 4:34 AM Hierarchical Level Query Example using SYS_CONNECT_BY_PATH SYS_CONNECT_BY_PATH: is valid only in hierarchical queries. It returns the path of a column value from root to node, with column values separated by char for each row returned by CONNECT BY condition. Both column and char can be any of the datatypes CHAR, VARCHAR2, NCHAR, or NVARCHAR2. The string returned is of VARCHAR2 datatype and is in the same character set as column. Examples The following example returns the path of employee names from employee Kochhar to all employees of Kochhar (and their employees): SELECT LPAD(' ', 2*level-1)||SYS_CONNECT_BY_PATH(last_name, '/') "Path" FROM employees START WITH last_name = 'Kochhar' CONNECT BY PRIOR employee_id = manager_id; Path --------------------------------------------------------------- /Kochhar /Kochhar/Greenberg /Kochhar/Greenberg/Faviet /Kochhar/Greenberg/Chen /Kochhar/Greenberg/Sciarra /Kochhar/Greenberg/Urman /Kochhar/Greenberg/Popp /Kochhar/Whalen /Kochhar/Mavris /Kochhar/Baer /Kochhar/Higgins /Kochhar/Higgins/Gietz Hierarchical Queries -CONNECT_BY_ROOT Examples Hierarchical Queries: CONNECT_BY_ROOT Examples The following example returns the last name of each employee in department 110, each manager above that employee in the hierarchy, the number of levels between manager and employee, and the path between the two: SELECT last_name "Employee", CONNECT_BY_ROOT last_name "Manager", LEVEL-1 "Pathlen", SYS_CONNECT_BY_PATH(last_name, '/') "Path" FROM employees WHERE LEVEL > 1 and department_id = 110 CONNECT BY PRIOR employee_id = manager_id;
  • 3.
    Employee Manager PathlenPath --------------- ------------ ---------- ----------------------------------- Higgins Kochhar 1 /Kochhar/Higgins Gietz Kochhar 2 /Kochhar/Higgins/Gietz Gietz Higgins 1 /Higgins/Gietz Higgins King 2 /King/Kochhar/Higgins Gietz King 3 /King/Kochhar/Higgins/Gietz Avoiding Collection Exceptions Avoiding Collection Exceptions In most cases, if you reference a nonexistent collection element, PL/SQL raises a predefined exception. Consider the following example: DECLARE TYPE NumList IS TABLE OF NUMBER; nums NumList; -- atomically null BEGIN /* Assume execution continues despite the raised exceptions. */ nums(1) := 1; -- raises COLLECTION_IS_NULL (1) nums := NumList(1,2); -- initialize table nums(NULL) := 3 -- raises VALUE_ERROR (2) nums(0) := 3; -- raises SUBSCRIPT_OUTSIDE_LIMIT (3) nums(3) := 3; -- raises SUBSCRIPT_BEYOND_COUNT (4) nums.DELETE(1); -- delete element 1 IF nums(1) = 1 THEN ... -- raises NO_DATA_FOUND (5) In the first case, the nested table is atomically null. In the second case, the subscript is null. In the third case, the subscript is outside the legal range. In the fourth case, the subscript exceeds the number of elements in the table. In the fifth case, the subscript designates a deleted element. The following list shows when a given exception is raised: Collection Exception Raised when... COLLECTION_IS_NULL you try to operate on an atomically null collection. NO_DATA_FOUND a subscript designates an element that was deleted, or a nonexistent element of an associative array. SUBSCRIPT_BEYOND_COUNT a subscript exceeds the number of elements in a collection. SUBSCRIPT_OUTSIDE_LIMIT a subscript is outside the allowed range.
  • 4.
    VALUE_ERROR a subscript isnull or not convertible to the key type. This exception might occur if the key is defined as a PLS_INTEGER range, and the subscript is outside this range. In some cases, you can pass invalid subscripts to a method without raising an exception. For instance, when you pass a null subscript to procedure DELETE, it does nothing. Also, you can replace deleted elements without raising NO_DATA_FOUND, as the following example shows: DECLARE TYPE NumList IS TABLE OF NUMBER; nums NumList := NumList(10,20,30); -- initialize table BEGIN nums.DELETE(-1); -- does not raise SUBSCRIPT_OUTSIDE_LIMIT nums.DELETE(3); -- delete 3rd element dbms_output.put_line(nums.COUNT); -- prints 2 nums(3) := 30; -- allowed; does not raise NO_DATA_FOUND dbms_output.put_line(nums.COUNT); -- prints 3 END; Posted by Asif at 1:28 AM ORACLE-BASE - Using Ref Cursors To Return Recordsets Using Ref Cursors To Return Recordsets The Reference Cursor which is also called the Cursor Variables is available since Oracle 7.3 the REF CURSOR type has been available to allow recordsets to be returned from stored procedures and functions. Oracle 9i introduced the predefined SYS_REFCURSOR type, meaning we no longer have to define our own REF CURSOR types. The example below uses a ref cursor to return a subset of the records in the EMP table. The following procedure opens a query using a SYS_REFCURSOR output parameter. Notice the cursor is not closed in the procedure. It is up to the calling code to manage the cursor once it has been opened. CREATE OR REPLACE PROCEDURE get_emp_rs (p_deptno IN emp.deptno%TYPE, p_recordset OUT SYS_REFCURSOR) AS BEGIN OPEN p_recordset FOR SELECT ename, empno, deptno FROM emp WHERE deptno = p_deptno ORDER BY ename; END GetEmpRS; / The resulting cursor can be referenced from PL/SQL as follows.
  • 5.
    SET SERVEROUTPUT ONSIZE 1000000 DECLARE l_cursor SYS_REFCURSOR; l_ename emp.ename%TYPE; l_empno emp.empno%TYPE; l_deptno emp.deptno%TYPE; BEGIN get_emp_rs (p_deptno => 30, p_recordset => l_cursor); LOOP FETCH l_cursor INTO l_ename, l_empno, l_deptno; EXIT WHEN l_cursor%NOTFOUND; DBMS_OUTPUT.PUT_LINE(l_ename || ' | ' || l_empno || ' | ' || l_deptno); END LOOP; CLOSE l_cursor; END; / Posted by Oracle PL/SQL - Raise User-Defined Exception With Custom SQLERRM - Stack Overflow User defined Exception with Naming and assigning the Error Code no. 1 declare 2 ex_custom EXCEPTION; 3 PRAGMA EXCEPTION_INIT( ex_custom, -20001 ); 4 begin 5 raise_application_error( -20001, 'This is a custom error' ); 6 exception 7 when ex_custom 8 then 9 dbms_output.put_line( sqlerrm ); 10* end; SQL> / ORA-20001: This is a custom error PL/SQL procedure successfully completed. Compound Triggers , Avoid Mutating-Table Error Using Compound Triggers to Avoid Mutating-Table Error You can use compound triggers to avoid the mutating-table error (ORA-04091) described in Trigger Restrictions on Mutating Tables. Scenario: A business rule states that an employee's salary increase must not exceed 10% of the average salary for the employee's department. This rule must be enforced by a trigger.
  • 6.
    Solution: Define acompound trigger on updates of the table hr.employees, as in Example 9-4. The state variables are initialized each time the trigger fires (even when the triggering statement is interrupted and restarted). Example 9-4 Compound Trigger that Avoids Mutating-Table Error CREATE OR REPLACE TRIGGER Check_Employee_Salary_Raise FOR UPDATE OF Salary ON Employees COMPOUND TRIGGER Ten_Percent CONSTANT NUMBER := 0.1; TYPE Salaries_t IS TABLE OF Employees.Salary%TYPE; Avg_Salaries Salaries_t; TYPE Department_IDs_t IS TABLE OF Employees.Department_ID%TYPE; Department_IDs Department_IDs_t; TYPE Department_Salaries_t IS TABLE OF Employees.Salary%TYPE INDEX BY VARCHAR2(80); Department_Avg_Salaries Department_Salaries_t; BEFORE STATEMENT IS BEGIN SELECT AVG(e.Salary), NVL(e.Department_ID, -1) BULK COLLECT INTO Avg_Salaries, Department_IDs FROM Employees e GROUP BY e.Department_ID; FOR j IN 1..Department_IDs.COUNT() LOOP Department_Avg_Salaries(Department_IDs(j)) := Avg_Salaries(j); END LOOP; END BEFORE STATEMENT;
  • 7.
    AFTER EACH ROWIS BEGIN IF :NEW.Salary - :Old.Salary > Ten_Percent*Department_Avg_Salaries(:NEW.Department_ID) THEN Raise_Application_Error(-20000, 'Raise too big'); END IF; END AFTER EACH ROW; END Check_Employee_Salary_Raise; Mutating Oracle triggers Avoiding Mutating triggers The insertto the childtable causedthe foreignkeytovalidate the dataonthe parent (whichfiredthe trigger) causingthe insertof the childtable toresultina mutatingtable erroron the parenttable. The Oracle mutatingtriggererroroccurs whena triggerreferencesthe table thatownsthe trigger, resultinginthe "ORA-04091: table name ismutating,trigger/functionmaynotsee it."message.  Don't use triggers - The bestway to avoidthe mutatingtable errorisnot to use triggers. While the object-orientedOracle provides"methods"thatare associatedwithtables,mostsavvyPL/SQL developersavoidtriggersunlessabsolutelynecessary.  Use an "after" or "insteadof" trigger- If youmust use a trigger,it'sbestto avoidthe mutating table errorby usingan "after"trigger,toavoidthe currency issuesassociatedwithamutatingtable. For example,usingatrigger":afterupdate onxxx",the original update hascompletedandthe table will not be mutating.  Re-work the trigger syntax - Dr. Hall has some greatnoteson mutatingtable errors,andoffers otherwaysto avoidmutatingtableswith acombinationof row-levelandstatement-leveltriggers.  Use autonomous transactions - You can avoidthe mutatingtable errorby markingyourtrigger as an autonomoustransaction,makingitindependentfromthe table thatcallsthe procedure. At the endof the day,the mutatingtable erroris usuallythe resultof apoor applicationdesignand mutatingtriggersshouldbe avoidedwheneverpossible. Steve Callanhasthese notesonthe ORA-04091 error: "Here isa problemmanydevelopersruninto:ORA-04091 table owner.table_name ismutating, trigger/functionmaynotsee it.In manycases,the cause of thiserror isdue to code withina triggerthat looksat or touchesthe data withinthe table the trigger isbeingcalledorinvokedfrom.
  • 8.
    The reasonOracle raisesthiserrorisrelatedtoone of Oracle'sprimarystrengthsasa relational database managementsystem.The particularstrengthinquestionhere isthatof havinga read consistentview of data. It isworthwhile tonote thatthisORA-04091 error occurs notonlyin the "pure"database development environment,butalsointhe Oracle toolstype of developmentenvironmentsuchasOracle SQL*Forms. AnothersolutionreliesonusinganINSTEAD-OFtriggerinsteadof the triggeryoumeanttouse when youreceivedthe error.Anothersolutionisactuallymore of apreventative measure,namely,usingthe righttype of triggerforthe taskat hand. Perhapsthe greateststrengthorutilityof anINSTEAD-OFtriggerisitsabilitytoupdate whatwould normallyappeartobe non-updateableviews.Simpleviews(prettymuchbasedonasingle base table) generallyare inherentlyupdateableviaDML statementsissuedagainstthe view. However,whenaviewbecomesmore complex (multiple tablesorviewsusedinvariousjoinconditions to create the newsingle view),there isagoodchance that many columns,asreferencedbythe view, lose their"updateable-ness." So,beingthe data dictionaryview/tablename triviawizardthat youare,youknow to querythe XXX_UPDATABLE_COLUMNSviews,substitutingUSER,ALL or DBA for XXXas applicable. There are exceptionstothisrule aboutviewsbeinginherentlyupdateable.The exceptions(or restrictions) include viewsthatuse aggregate functions;groupfunctions;use of the DISTINCTkeyword; use of GROUP BY, CONNECTBY or STARTWITH clauses;anduse of some joins.Inmanycases,use of the INSTEAD-OFtriggerfeature allowsyoutoworkaroundthese restrictions. INSTEAD-OFtriggersare alsouseful forFormsdevelopersbecause formsare commonlybasedonviews. The INSTEAD-OFtrigger,beinga"real"trigger,andnot a true form trigger,isstoredonthe server."