Oracle PL/SQL Comprehensive Guide
Oracle PL/SQL Comprehensive Guide
1. Module 1 – 20 Questions
2. Module 2 – 30 Questions (include the topics from Module 1)
3. Module 3 – 40 Questions (include the topics from Module 1,2)
4. Hands -on Assessment
5. APEX certification
6. My Competency
Day 1
PL/SQL
❖ Procedural/Programming Language with the extension of SQL
❖ It contains SQL & Non SQL queries as a PL/SQL block
❖ All SQL queries in PL/SQL block can be executed by Database engine. Non-SQL statements
will be executed by PL/SQL engine
❖ Non - SQL statement include conditional, looping, calculations etc.,
❖ There is a connection between the 2 consecutive statements in the PL/SQL block. If a block
contains 100 lines of code, there is an error in 10th line then the entire block will not be
executed
❖ DCL statements are not allowed in the PL/SQL
❖ DDL statements cannot be used directly in PL/SQL blocks.
Pros:
➢ Network traffic will be less if we keep the multiple SQL/Non SQL statements inside the
PL/SQL
➢ Provides security
➢ Nesting of a block is allowed
➢ It support Object oriented Programming language like Polymorphism (Function
Overloading)
➢ Reduces the compilation time
➢ It can be called from any front end application like Java, .net, oracle forms, oracle
reports
1. Unnamed/Anonymous block
2. Named block
a. Stored Sub programs -> Procedures , Functions
b. Packages -> collection of procedures, functions, variables
c. Triggers
❖ Named block can be stored in the database, but unnamed block cannot be stored
❖ Named blocks can be compiled once, executed many times. But Unnamed block compile and
execute every time
❖ Named block can be called from any front end application, but we cannot call the unnamed
block from any front end application
UnNamed block:
BOTH inner and outer block variable has the same name:
To display the outer block variable inside the inner block
BOOLEAN DATA TYPE: (PL/SQL DATA TYPE)
The above block will give compilation error, because boolean values cannot be printed. It can be
used only as a condition
Conditional Statements (IF , ELSE , ELSIF)
Conditional construct:
IF condition:
Nested IF condition:
OR
IF - ELSIF - ELSE
LOOPS:
EXIT is used to come out of the loop, which is mandatory for simple loop.
CONTINUE is used to skip the checked value and continue the remaining ones.
Simple Loop:
WHILE LOOP:
OR
FOR LOOP:
For autodecrementation by 1,
Note: Don't declare a local variable as that of a loop variable. We cannot modify the loop
variable.
Day 2
Substitution variable:
SELECT STATEMENT IN PL/SQL BLOCK:
%TYPE => will automatically assign the datatype & size of a column to the given variable
5. To display the entire row of a particular employee.
There are 11 columns in [Link] table. To display the entire row, we have to create 11
variables for each column. To avoid this , we can use %ROWTYPE
RECORD:
Struct emp
{
Int empid;
Char name[30];
Int salary;
};
Records in PL/SQL:
Collections:
Associative array:
❖ Its like an array concept in programming
❖ Collection of elements under one name
❖ Its unbounded array
❖ Data in associative array will be SPARSE is nature -> data may be continuously or may not be
stored
❖ Created, used only inside the PL/SQL block
❖ We need an index to access the values. The indexes may or may not be continuous
a. Create an associative array which has only the country list
DECLARE
TYPE MY_ARR IS TABLE OF VARCHAR2(40) INDEX BY PLS_INTEGER;
MY_OBJ MY_ARR;
X PLS_INTEGER;
BEGIN
MY_OBJ(-77):='INDIA';
MY_OBJ(0):='US';
MY_OBJ(9):='JAPAN';
MY_OBJ(2):='CHINA';
MY_OBJ(-23):='RUSSIA';
-- TO PRINT SPECIFIC VALUE
DBMS_OUTPUT.PUT_LINE(MY_OBJ(2));
-- TO PRINT THE TOTAL NO OF ELEMENTS
DBMS_OUTPUT.PUT_LINE('TOTAL COUNT ='||MY_OBJ.COUNT);
-- TO PRINT THE FIRST INDEX
DBMS_OUTPUT.PUT_LINE('FIRST INDEX ='||MY_OBJ.FIRST);
-- TO PRINT THE VALUE IN THE FIRST INDEX
DBMS_OUTPUT.PUT_LINE('FIRST INDEX VALUE ='||MY_OBJ(MY_OBJ.FIRST));
-- TO PRINT ALL THE ELEMENTS
X:=MY_OBJ.FIRST;
WHILE X IS NOT NULL
LOOP
DBMS_OUTPUT.PUT_LINE(MY_OBJ(X));
X:=MY_OBJ.NEXT(X);
END LOOP;
END;
EXISTS FUNCTION, OVERWRITE OF DATA:
DECLARE
TYPE MY_ARR IS TABLE OF VARCHAR2(40) INDEX BY PLS_INTEGER;
MY_OBJ MY_ARR;
X PLS_INTEGER;
BEGIN
MY_OBJ(-77):='INDIA';
MY_OBJ(0):='US';
MY_OBJ(9):='JAPAN';
MY_OBJ(-77):='CHINA';
MY_OBJ(-23):='RUSSIA';
-- TO PRINT SPECIFIC VALUE, CHECK THE INDEX AND THEN PRINT
IF(MY_OBJ.EXISTS(2)) THEN
DBMS_OUTPUT.PUT_LINE(MY_OBJ(2));
ELSE
DBMS_OUTPUT.PUT_LINE('NO SUCH DATA');
END IF;
-- TO PRINT THE TOTAL NO OF ELEMENTS
DBMS_OUTPUT.PUT_LINE('TOTAL COUNT ='||MY_OBJ.COUNT);
-- TO PRINT THE FIRST INDEX
DBMS_OUTPUT.PUT_LINE('FIRST INDEX ='||MY_OBJ.FIRST);
-- TO PRINT THE VALUE IN THE FIRST INDEX
DBMS_OUTPUT.PUT_LINE('FIRST INDEX VALUE ='||MY_OBJ(MY_OBJ.FIRST));
-- TO PRINT ALL THE ELEMENTS
X:=MY_OBJ.FIRST;
WHILE X IS NOT NULL
LOOP
DBMS_OUTPUT.PUT_LINE(MY_OBJ(X));
X:=MY_OBJ.NEXT(X);
END LOOP;
END;
DECLARE
TYPE MY_ARR IS TABLE OF NUMBER INDEX BY VARCHAR2(40);
MY_OBJ MY_ARR;
X VARCHAR2(40);
BEGIN
MY_OBJ('INDIA'):=65000;
MY_OBJ('US'):=32000;
MY_OBJ('JAPAN'):=28000;
MY_OBJ('CHINA'):=62000;
MY_OBJ('RUSSIA'):=45000;
-- TO PRINT SPECIFIC VALUE, CHECK THE INDEX AND THEN PRINT
IF(MY_OBJ.EXISTS('US')) THEN
DBMS_OUTPUT.PUT_LINE(MY_OBJ('US'));
ELSE
DBMS_OUTPUT.PUT_LINE('NO SUCH DATA');
END IF;
-- TO PRINT THE TOTAL NO OF ELEMENTS
DBMS_OUTPUT.PUT_LINE('TOTAL COUNT ='||MY_OBJ.COUNT);
-- TO PRINT THE FIRST INDEX
DBMS_OUTPUT.PUT_LINE('FIRST INDEX ='||MY_OBJ.FIRST);
-- TO PRINT THE VALUE IN THE FIRST INDEX
DBMS_OUTPUT.PUT_LINE('FIRST INDEX VALUE ='||MY_OBJ(MY_OBJ.FIRST));
-- TO PRINT ALL THE ELEMENTS
X:=MY_OBJ.FIRST;
WHILE X IS NOT NULL
LOOP
DBMS_OUTPUT.PUT_LINE(MY_OBJ(X));
X:=MY_OBJ.NEXT(X);
END LOOP;
END;
Create an associative array having more than one column & perform the DELETE operation
DECLARE
TYPE MY_REC IS RECORD(NAME VARCHAR2(40),MARKS NUMBER);
TYPE MY_ARR IS TABLE OF MY_REC INDEX BY BINARY_INTEGER;
MY_OBJ MY_ARR;
BEGIN
MY_OBJ(0).NAME:='ARAV';
MY_OBJ(0).MARKS:=77;
MY_OBJ(8).NAME:='HARSHA';
MY_OBJ(8).MARKS:=45;
MY_OBJ(-12).NAME:='JANE';
MY_OBJ(-12).MARKS:=88;
-- TO PRINT THE FIRST INDEX
DBMS_OUTPUT.PUT_LINE('FIRST INDEX ='||MY_OBJ.FIRST);
-- TO PRINT THE VALUE IN THE FIRST INDEX
DBMS_OUTPUT.PUT_LINE('FIRST INDEX VALUE ='||MY_OBJ(MY_OBJ.FIRST).NAME);
-- TO DELETE THE SPECIFIC DATA
MY_OBJ.DELETE(8);
-- TO PRINT THE TOTAL NO OF ELEMENTS
DBMS_OUTPUT.PUT_LINE('TOTAL COUNT ='||MY_OBJ.COUNT);
END;
OR
DECLARE
TYPE MY_REC IS RECORD(NAME VARCHAR2(40),MARKS NUMBER);
TYPE MY_ARR IS TABLE OF MY_REC INDEX BY BINARY_INTEGER;
MY_OBJ MY_ARR;
BEGIN
MY_OBJ(0).NAME:='ARAV';
MY_OBJ(0).MARKS:=77;
MY_OBJ(8).NAME:='HARSHA';
MY_OBJ(8).MARKS:=45;
MY_OBJ(-12).NAME:='JANE';
MY_OBJ(-12).MARKS:=88;
-- TO PRINT THE FIRST INDEX
DBMS_OUTPUT.PUT_LINE('FIRST INDEX ='||MY_OBJ.FIRST);
-- TO PRINT THE VALUE IN THE FIRST INDEX
DBMS_OUTPUT.PUT_LINE('FIRST INDEX VALUE ='||
MY_OBJ(MY_OBJ.FIRST).NAME||' '||MY_OBJ(MY_OBJ.FIRST).MARKS);
-- TO DELETE THE SPECIFIC DATA
MY_OBJ.DELETE(8);
-- TO PRINT THE TOTAL NO OF ELEMENTS
DBMS_OUTPUT.PUT_LINE('TOTAL COUNT ='||MY_OBJ.COUNT);
END;
VARRAY:
In other programming language, int a[10] -> we can store 10 integer elements starting with index zero
In varray -> it can have numbers, characters, date, starting with index one
Examples:
OR
OR
Partially filled varray:
OR
Inside SQLPlus (Oracle Linux) / outside the PL/SQL block
<Separate Creation of VARRAY Type Variable>
Nested table:
Another options:
3. Create the main table which has one of the column as nested table column
Day 3
Cursor
A temporary memory area which is automatically opened for all DML operations. For Select
statement we have to open the cursor area explicitly.
Types of Cursor :
❖ Implicit
❖ Explicit
A. Implicit for all 3 DML operations(INSERT, UPDATE, and DELETE). Explicit for Select
Statement.
B. Implicit cursor has their name SQL. Explicit cursor is created by the user and they can have
any name.
C. Implicit cursor area will be opened , fetch all the rows , and perform the DML automatically.
For explicit cursors ,all the functions can be done by the user.
Cursor Attributes
● %ISOPEN : Returns True if the cursor area is open.
● %FOUND : Returns True if you are able to find the record.
● %NOTFOUND : Returns True if you are NOT able to find the record.
● %ROWCOUNT : Returns the total number of rows affected by DML OR SELECT statement.
Implicit Cursor
Demo :
Explicit Cursor
1. To display first_name of employees who are working in department no 90.
Solution :
Simple Loop
While Loop
For Loop
Fastest and Required less syntaxes to write as OPEN , FETCH , CLOSE cursors are done
automatically
To reduce the race condition and bring consistency in the changes to the tuples in the database we
can use For Update clause with wait and Nowait clause.
Wait X : Wait for X seconds to check where any another session doing changes in the same sets of
data, if fails returns an error.
Scenario:
If we want to update the salary of employees:
a ) for odd no : salary + 1000.
b ) for even no : salary + 2000;
Upon applying COMMIT(CHANGE ACCEPTED) or ROLLBACK(CHANGE REJECTED) to any of
those sessions cause to release the lock and current session’s changes can be reflected.
Up to this , all of the cursors are Static Cursors.
Exception
Runtime errors are called Exceptions in Oracle. Exception can be handled in Exception Block.
If it is not handled, it will lead to abnormal termination , which affects the performance of the
PL/SQL Block.
Types of Exception
➢ Pre-Defined Exception
➢ Non-Predefined Exception
➢ User-Defined Exception
Pre-Defined Exception :
● These exceptions are already defined in the ORACLE SERVER with unique error codes and
error messages.
● It is automatically invoked and handled by the ORACLE SERVER.
● It is already stored in th database.
● SQLCODE - A predefined pseudocolumn which gives the unique error codes.
● SQLERRM - A predefined pseudocolumn which gives the unique error messages.
Example :
To resolve this problem, we can write an exception block
Not necessary to give SQLCODE , SQLERRM everytime. We need at least one statement after
the WHEN clause.
We will get an exception in the above example if we don’t handle the exception correctly.
Day 4
Non Predefined Exception
1. Its an exception created by the user
2. It can be invoked and handled by the oracle server automatically.
3. We can access the predefined exceptions indirectly through the non-predefined exceptions
More than one handlers for one exception raises Compilation Error. In the above example ,
MY_EXP already became a NO_DATA_FOUND exception , so to give again NO_DATA_FOUND
separately raises the Compilation error.
User Defined Exception
● It’s an exception which can be created , invoked and handled by the user.
● This exception is used for Logical Checking of data.
The above block will work fine if the age is between 20 to 60. If not , the it will throw the error. To
resolve this, we can use two methods
A. IF-ELSE Block
DECLARE
FNAME empl.FIRST_NAME%TYPE;
BEGIN
SELECT FIRST_NAME
INTO FNAME
FROM empl
WHERE EMPLOYEE_ID = 100;
DBMS_OUTPUT.PUT_LINE(FNAME);
DECLARE
SAL [Link]%TYPE;
BEGIN
SELECT SALARY
INTO SAL
FROM empl
WHERE EMPLOYEE_ID = 101;
DBMS_OUTPUT.PUT_LINE(SAL);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('INNER BLOCK EXCEPTION');
END;
DBMS_OUTPUT.PUT_LINE('INNER BLOCK OVER');
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('OUTER BLOCK EXCEPTION');
END;
Scenario - 1 :Both the blocks will get executed , since there are no errors. In case we
get an exception in the INNER BLOCK
Scenario - 2 : We got an exception in the OUTER BLOCK , not in the inner block.
Scenario - 3 : We got an exception in the inner block, but it’s not handled in the INNER
BLOCK , it was handled in the OUTER BLOCK.
DECLARE
FNAME empl.FIRST_NAME%TYPE;
BEGIN
SELECT FIRST_NAME
INTO FNAME
FROM empl
WHERE EMPLOYEE_ID = 100;
DBMS_OUTPUT.PUT_LINE(FNAME);
DECLARE
SAL [Link]%TYPE;
BEGIN
SELECT SALARY
INTO SAL
FROM empl
WHERE EMPLOYEE_ID = 1010;
DBMS_OUTPUT.PUT_LINE(SAL);
EXCEPTION
WHEN TOO_MANY_ROWS THEN
DBMS_OUTPUT.PUT_LINE('INNER BLOCK EXCEPTION');
END;
DBMS_OUTPUT.PUT_LINE('INNER BLOCK OVER');
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('OUTER BLOCK EXCEPTION');
END;
DECLARE
FNAME empl.FIRST_NAME%TYPE;
BEGIN
SELECT FIRST_NAME
INTO FNAME
FROM empl
WHERE EMPLOYEE_ID = 100;
DBMS_OUTPUT.PUT_LINE(FNAME);
DECLARE
SAL [Link]%TYPE;
BEGIN
SELECT SALARY
INTO SAL
FROM empl
WHERE EMPLOYEE_ID = 1010;
DBMS_OUTPUT.PUT_LINE(SAL);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('INNER BLOCK EXCEPTION');
RAISE; -- This is called Re-Raising the same exception
END;
DBMS_OUTPUT.PUT_LINE('INNER BLOCK OVER');
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('OUTER BLOCK EXCEPTION');
END;
By Knowing the OUTER BLOCK EXCEPTION (different from Inner Block), we can RAISE that
error from inner block.
DECLARE
FNAME empl.FIRST_NAME%TYPE;
BEGIN
SELECT FIRST_NAME
INTO FNAME
FROM empl
WHERE EMPLOYEE_ID = 100;
DBMS_OUTPUT.PUT_LINE(FNAME);
DECLARE
SAL [Link]%TYPE;
BEGIN
SELECT SALARY
INTO SAL
FROM empl
WHERE EMPLOYEE_ID = 1010;
DBMS_OUTPUT.PUT_LINE(SAL);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('INNER BLOCK EXCEPTION');
RAISE TOO_MANY_ROWS; -- This will Invoke Outer Block
Exception
END;
DBMS_OUTPUT.PUT_LINE('INNER BLOCK OVER');
EXCEPTION
WHEN TOO_MANY_ROWS THEN
DBMS_OUTPUT.PUT_LINE('OUTER BLOCK EXCEPTION');
END;
Raise is a statement which will invoke pre-defined , non-predefined and user-defined exception also.
Notes :
❖ If both Inner Block & Outer Block has no errors , no exception will be invoked
❖ If outer block is having exception , the control will automatically go to the outer block
exception , even the inner block is correct. If the exception is handled in the outer block ,it
is normal termination , otherwise it is abnormal termination.
❖ If the Outer block is correct but the inner block is having an exception , then the control
will go the inner block exception. If the exception is handled in the inner block, it won’t go to
the outer block exception. If its not handled , it will check into Outer Block Exception.
❖ If we want to go to the Outer Block exception From the Inner block directly , Use RAISE
statement.
❖ RAISE is a statement which will invoke all kinds of exceptions but its MANDATORY for
USER-DEFINED EXCEPTIONS.
ECODE VARCHAR2(100),
EMESSAGE VARCHAR2(200)
);
DECLARE
FNAME empl.FIRST_NAME%TYPE;
e_code ERROR_DETAILS.ECODE%TYPE;
e_message ERROR_DETAILS.EMESSAGE%TYPE;
BEGIN
SELECT FIRST_NAME
INTO FNAME
FROM empl
DBMS_OUTPUT.PUT_LINE(FNAME);
EXCEPTION
e_code:=SQLCODE;
e_message:=SQLERRM;
INSERT INTO ERROR_DETAILS VALUES(e_code,e_message);
END;
Named Block
● Can be stored in the database.
● It can be called from any frontend application.
● Compiled once , executed many times.
1. Procedures may or may not return a value. Function must return at least one value.
2. Procedures can’t be called as a part of SQL Queries. Functions can be called as a part
of SQL queries.
Parameters OR Arguments
3 types
1. IN - The procedure or function can be called through IN parameter. Its a Read-Only
Parameter.
2. OUT - procedure or function can return a value through OUT parameter. Default initial value is
NULL
3. IN OUT - procedure or function can return a value through IN OUT parameter. This parameter
can accept values from User and modify the data in the same parameter.
E.g: In sum of n natural numbers , value of sum is initially 0 and it got updated in the
same variable. Its an example of IN OUT parameter.
BEGIN
SELECT FIRST_NAME
INTO FNAME
FROM empl
WHERE EMPLOYEE_ID = &ID;
DBMS_OUTPUT.PUT_LINE(FNAME);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('invalid employee');
END;
EXEC EMP_PROC
In the above example , we use the substitution variable to get the data from the user. But substitution
variable is a pre-compiler variable so it will ask only once for all named block. Don’t use a substitution
variable in the named block which is of no use.
BEGIN
SELECT FIRST_NAME
INTO FNAME
FROM empl
WHERE EMPLOYEE_ID = ID;
DBMS_OUTPUT.PUT_LINE(FNAME);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('invalid employee');
END;
EXEC EMP_PROC(105)
EXEC EMP_PROC(110)
Procedure on a Cursor
BEGIN
UPDATE empl
SET SALARY = SALARY + 1000
WHERE EMPLOYEE_ID = ID;
COMMIT;
FOR it IN emp_cur
LOOP
DBMS_OUTPUT.PUT_LINE(it.FIRST_NAME||' '||[Link]);
END LOOP;
END;
EXEC PROC_UPDATE_EMP(100);
EXEC PROC_UPDATE_EMP(110);
Function Without Parameters
Create a Function
Using DUAL Table , PL/SQL Block & Bind Variable
Using Procedures
Another Example
Function With Parameters
In Parameter
A and B are IN Parameters by default and C is a local variable for ADD_FUNC.
Day 5
OUT Parameter
● Out parameter is used to return a value from the procedure or function.
● We can initialize the out parameters outside the block but it always takes NULL.
● The value can be modified only inside the Named blocks
Notes :
1. We can call functions/procedures from any other Named Block , Unnamed Block , Bind
Variables.
2. Named Block always starts with Create command.
Procedure with OUT parameters
DECLARE
X empl.FIRST_NAME%TYPE;
Y [Link]%TYPE;
BEGIN
EMP_PROC(100,X,Y);
DBMS_OUTPUT.PUT_LINE(X||' '||Y);
END;
Procedure
We can use OUT or IN OUT parameters in Functions only if we try to return more than one values.
Function
Can’t Use SELECT statement for Functions/Procedures Having OUT parameters
DECLARE
X empl.FIRST_NAME%TYPE;
Y [Link]%TYPE;
BEGIN
Y:=EMP_FUNC(100,X);
DBMS_OUTPUT.PUT_LINE(X||' '||Y);
END;
IN OUT parameter
Q.
DECLARE
A NUMBER:=10;
R NUMBER;
BEGIN
SUM_N_PROC(A,R);
END;
● The procedure or function which is defined locally inside the another procedure or
function.
● This local subprogram can be called only within procedure or function body.
Notes : For the inner procedure , we don’t have to start with create or replace . Same goes for
nested functions , the inner function won’t start with create or replace.
Procedures
Functions
Procedure as local subprogram inside Function
Function as local subprogram inside Procedure
Named parameters
When we try calling the above procedure like below, we will get an error.
Default in tables:
Packages:
Building blocks/Components:
1. Package Header /Specification (Mandatory) -> It contains all the prototype/declaration
of all the members (variables, procedures, functions
2. Package Body (Optional) -> It contains the definition/coding block of the members
Note:
➢ If we keep 1000 procedures, 1000 functions inside a package, it creates only 2 entries,
one for header and one for body
➢ If we drop the package header/specification, the package body also dropped
along with that.
➢ If we drop the package body, then the header/specification still remains the same.
It won't be dropped
➢ If we drop the table in which package is created, package become invalid
Demos:
❖ The codes given in the BEGIN and END block of Package body will be executed
only once (First time alone). This is called ONE TIME ONLY PROCEDURE
DEMO 1:
END;
DEMO 2:
Variables in Packages:
RNDS
DEMO 2
is
x number:=100;
return number;
end;
as
fname ACC_EMP.first_name%type;
BEGIN
dbms_output.put_line(x);
x:=500;
dbms_output.put_line(fname);
end demo_proc;
return number
as
begin
dbms_output.put_line(x);
c:=a+b;
return c;
end demo_func;
BEGIN
dbms_output.put_line('hello');
end;
/
PRAGMA RESTRICT_REFERENCES
PRAGMA RESTRICT_REFERENCES is used to check the purity level of the code.
We are restricting the accessing of variables & tables inside the members of the package.
Syntax:
PRAGMA RESTRICT_REFERENCES(procedure_name/function_name,purity_level)
Purity level:
1. RNDS - Read No to Database State (SELECT is not allowed, but DML’s are allowed)
2. WNDS - Write No to Database State( Only select is allowed, no DML)
3. RNPS - Read No to Package State(Reading of Package variable is not allowed)
4. WNPS - Write No to Package State( Modifying the package variable is not
allowed)
RNPS
-- PACKAGE BODY
PRAGMA RESTRICT_REFERENCES(P1,RNDS,WNDS);
PRAGMA RESTRICT_REFERENCES(F1,RNDS);
PRAGMA RESTRICT_REFERENCES(P5,RNDS,RNPS,WNDS,WNPS) -> NO SELECT, NO DML,
NO READING OF A VARIABLE, NO MODIFICATION OF A VARIABLE
Day 6
Triggers:
❖ Its an automatic firing event happen whenever there is a change in the database
❖ TCL statements(commit,rollback, savepoint) are not allowed in triggers
❖ We cannot create a trigger for select statement
❖ If we create a trigger for the table, and the table is dropped then the trigger is automatically
dropped.
Components:
1. Trigger Name
2. Trigger Type
3. Triggering Events -
INSERT/UPDATE/DELETE/CREATE/DROP/LOGON/LOGOFF/STARTUP/SHUTDOWN
4. Trigger Timings - BEFORE/AFTER
5. Trigger Body
TYPES:
DML TRIGGERS:
TYPES:
1. STATEMENT LEVEL TRIGGER - this trigger will fire only once whether the DML operation is
success/failed
2. ROW LEVEL TRIGGER - this will be fired depending on the no of rows affected by DML
operations.
For example,
1. if 5 rows are affected, STATEMENT LEVEL trigger will fire only once, but ROW LEVEL trigger
will fire 5 times.
2. If no rows are affected, STATEMENT LEVEL trigger will fire only once, but ROW LEVEL
trigger will not fire
Notes : DBA can only perform DDL triggers , not DML triggers
TO REMOVE A TRIGGER:
ROW LEVEL TRIGGERS:
FOR 3 ROWS,
FOR ZERO ROWS, ROW LEVEL TRIGGER WILL NOT FIRE
DEMO 2:
:OLD, :NEW -> Is applicable only in row level trigger
Scenarios:
1. Trigger should not be fired even though the particular event occurs
If we issue the update statement, the trigger will not fire
4. If we create 2 BEFORE STATEMENT LEVEL triggers, the one which created last will be fired
first.
CREATE OR REPLACE TRIGGER T1
AFTER
UPDATE
ON
SAMPLE_EMP
BEGIN
DBMS_OUTPUT.PUT_LINE('AFTER STATEMENT LEVEL');
END;
/
❖ BEFORE STATEMENT
❖ BEFORE ROW
❖ AFTER ROW
❖ BEFORE ROW
❖ AFTER ROW
❖ BEFORE ROW
❖ AFTER ROW
❖ AFTER STATEMENT
6. After disabling all the triggers, we are a creating the new trigger as below:
Recursive Error
It becomes infinite because we are using the update statement on the same table inside the trigger.
To resolve this,
1. When we issue an outer update command on salary, the entire table got locked. When we
come inside the trigger block, we are trying to update the commission_pct column on a locked
table. This leads to a deadlock situation called mutation.
2. Mutation occurs only with row level not with statement level
Even we tried to create the trigger like below, still its the same.
Notes : To stop Mutation Error , We need Compound Trigger (Not in our syllabus)
OR
Simultaneous DML operations with one or more than one third party tables.
Solution : DML on particular columns
Day 7
Consider the below scenario:
This trigger will be fired for all departments. But we want to fire the trigger only for department id 30.
This will be resolved by CONDITIONAL TRIGGERS
Conditional triggers:
Scenario 2:
1. Create a procedure which accepts the department id and print all the first name of the
employees who are working in that department
2. The above procedure must be called on Week end(saturday and sunday) automatically
Scenario 3:
1. Create a procedure which accepts the department id and print all the first name of the
employees who are working in that department
2. The above procedure must be called whenever we tried the UPDATE on the
EMPLOYEES table on Week end(saturday and sunday) automatically
Scenario:
DEMO 1:
DEMO 2:
INSERTING,UPDATING,DELETING - Conditional Predicates
Instead of triggers:
Consider the given where we can insert a row in the table through view:
We are going to stop inserting the record through view. This can be done by using INSTEAD OF
TRIGGER
DDL/SYSTEM TRIGGERS
To display the first name of all the employees who are working specific department using the below
logics
1. Cursor
2. Using select in FOR loop
3. BULK COLLECT
Method 2:
Method 3:
We can use BULK COLLECT in our PL/SQL COLLECTIONS i.e., Associative array, Nested table,
VARRAY
The above is an example of a nested [Link] same thing can be achieved by Associative array.
DECLARE
TYPE DEPT_CUR IS REF CURSOR RETURN DEPARTMENTS%ROWTYPE;
TYPE EMP_REC IS RECORD (EID NUMBER,ENAME VARCHAR2(40),DNO NUMBER,SAL NUMBER);
DEPT_CUR_NEW DEPT_CUR;
D DEPARTMENTS%ROWTYPE;
E EMP_REC;
BEGIN
OPEN DEPT_CUR_NEW FOR SELECT * FROM DEPARTMENTS;
LOOP
FETCH DEPT_CUR_NEW INTO D;
EXIT WHEN DEPT_CUR_NEW%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(D.DEPARTMENT_NAME);
END LOOP;
CLOSE DEPT_CUR_NEW;
OPEN DEPT_CUR_NEW FOR SELECT EMPLOYEE_ID,FIRST_NAME,DEPARTMENT_ID,SALARY FROM
EMPLOYEES;
LOOP
FETCH DEPT_CUR_NEW INTO E;
EXIT WHEN DEPT_CUR_NEW%NOTFOUND;
DBMS_OUTPUT.PUT_LINE([Link]||' '||[Link]);
END LOOP;
CLOSE DEPT_CUR_NEW;
END;
/
But the below code will give an compilation error because the structure of DEPARTMENTS &
EMPLOYEES are different
DECLARE
TYPE DEPT_CUR IS REF CURSOR RETURN DEPARTMENTS%ROWTYPE;
DEPT_CUR_NEW DEPT_CUR;
D DEPARTMENTS%ROWTYPE;
E EMPLOYEES%ROWTYPE;
BEGIN
OPEN DEPT_CUR_NEW FOR SELECT * FROM DEPARTMENTS;
LOOP
FETCH DEPT_CUR_NEW INTO D;
EXIT WHEN DEPT_CUR_NEW%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(D.DEPARTMENT_NAME);
END LOOP;
CLOSE DEPT_CUR_NEW;
OPEN DEPT_CUR_NEW FOR SELECT * FROM EMPLOYEES;
LOOP
FETCH DEPT_CUR_NEW INTO E;
EXIT WHEN DEPT_CUR_NEW%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(E.FIRST_NAME||' '||[Link]);
END LOOP;
CLOSE DEPT_CUR_NEW;
END;
/
Notes:
❖ We need a RETURN in REF cursor if the multiple tables follow the same structure of columns.
❖ If tables are having different structure, we can use REF CURSOR without RETURN
clause
❖ If we have RETURN clause, it is said to be strongly typed REF CURSOR, otherwise it is
Weakly typed REF CURSOR.