Вы находитесь на странице: 1из 188

ORACLE PL/SQL

Table of Contents
Topic No. 1 2 3 4 5 7 8 9 10 11 12 Topic Name PL/SQL Features and Programming Basics PLSQL Records Cursors PLSQL Tables Bulk Binding Exceptions Procedures Functions Packages Triggers Oracle Suppiled Packages: 1) DBMS_SQL 2) DBMS_DDL Package 3) DBMS_JOB Package 4)UTL_File Package 5) DBMS_Session Managing Dependencies Ref Cursor Types VArray Nested Table Assignments Programming Basics Cursors PLSQL Tables Exception Handling Procedures Functions Packages Triggers Dynamic SQL Varray Nested Table Page No. 3 27 30 44 48 51 59 74 79 101 124 131 133 135 138 142 148 153 161 167 173 175 176 177 179 180 182 183 184 185 186

13 14 15 16 17 1 2 3 4 5 6 7 8 9 10 11

Topic No. 1 PL/SQL Features and Programming Basics Index


PL/SQL features -- PL/SQL is an extension of SQL It is an application development language containing procedural statements and commands along with SQL commands It bridges the gap between database technology and procedural programming languages It allows you to process data using flow control statements like iterative loops and conditional branching Uses procedural techniques of control, looping and branching Supports SQL i.e. cursor operations, exceptions, functions and transactional commands Variables and constants, robust error handling and functions Adds functionality to non-procedural tools such as SQL*Forms Developers using SQL*Forms can enter an entire PL/SQL block using a single trigger Structure of PL/SQL Standard PL/SQL code segment is called a Block A block consists of three parts or sections Declaration Part Executable Part Exception Handling Part
DECLARE Declarations BEGIN Executable Statements EXCEPTION Exception Handlers END; Sections of a PL/SQL Block

Declaration Part optional part where variables are defined 3

Executable Part mandatory part which consists of executable statements Exception Handling Part optional part which consists of code for handling errors (runtime) Pl/SQL Files - PL/SQL programs can be written in any editor and saved as files with .sql extension Can also use ED command in SQL*Plus to create a PL/SQL program file Use the @ <filename> command to execute a PL/SQL program file o Variables -Used to store results of a query for later processing, or to calculate values to be inserted into database tables Can be used anywhere in an expression, either in SQL or PL/SQL statements Must be declared before referencing it in other statements, including other declarative statements Are declared by specifying the name along with the datatype Can be declared to be of any datatype native to Oracle Examples oldfare NUMBER(5); m_name VARCHAR(15);

(Note Set Serveroutput On has to be given when a session starts for displaying the output statements_) dbms_output.put_line(String as a parameter) Till 9i we could have 255 characters per line. Now from Oracle 10g the limit is drastically increased to 32767 declare x number; begin x := 67; dbms_output.put_line(x); dbms_output.put_line('The value of x is '|| x); end; Declaring variable in declare block. Assigning value in in begin block using := . Output statement is dbms_output.put_line Concatenation operator is || Command terminator is ; after end Declaring and initializing variables together 4

declare y number := 100; begin dbms_output.put_line('The value of y is '|| y); end; -------------------------------------------------------------------------------

Taking value from the user using & declare z number; a varchar2(10); begin z := &z; a := '&a'; dbms_output.put_line('Z is '|| z); dbms_output.put_line('A is '|| a); end; ------------------------------------------------------------------------------/*Cannot declare or initialize more than one variable simultaneously*/ declare a number; b number; c number; begin a := 67; b := 90; c := 87; dbms_output.put_line(a); dbms_output.put_line(b); end; A constant number has to declared and initialized in the declare block only using CONSTANT keyword. Value cannot be changed declare r CONSTANT number :=100; begin /* r := r + 100; Not possible*/ dbms_output.put_line(r); end; /*Assigning value to variable from a column of a table using select into clause*/ declare

x number; begin Select sal Into x from emp where ename = 'SMITH'; dbms_output.put_line('Salary of Smith is '|| x); end;

/* Selecting ename,sal from emp Use of more than one columns value with Into clause*/ declare n varchar2(50); s number; begin select ename, sal Into n, s from emp where ename = 'SMITH'; dbms_output.put_line(n); dbms_output.put_line(s); end; % Type Attribute Provides datatype of a variable or column Useful when declaring a variable that refers to a column in a database exact datatype of column need not be known if column definition changes, variable datatype changes accordingly at runtime Example oldfare fare.first_fare%TYPE; newfare oldfare%TYPE; declare a emp.ename%type; b emp.sal%type; c emp.deptno%type; /*Using %TYPE attribute for variable data type*/ begin select ename,sal,deptno into a,b,c from emp where ename = 'KING';

dbms_output.put_line(a ||'-'|| b ||'-' || c); end; %RowType Attribute Useful when declaring a record variable having same structure as a row in a table or view, or as a row fetched from a cursor Fields in the record have same names and datatypes as the columns in the table/view Example emp_rec employee%ROWTYPE; A specific field can be referenced using emp_rec.emp_num; declare E emp%rowtype; /*rowtype attribute holds the datatype of the columns of the entire row*/ begin select * INTO E from emp where ename = 'MARTIN'; dbms_output.put_line(E.sal); dbms_output.put_line(E.ename); dbms_output.put_line(e.deptno); end; Conditional Statements 1) Case Construct Whenever Case is used in a PLSQL block then Case . End Case has to be used. Declare X number; Begin X := &x; Case X When 1 then Dbms_output.put_line(A); Else Dbms_output.put_line(B); End Case; End; 2) IF The selection structure tests a condition, then executes one sequence of statements instead of another, depending on the condition 7

There are three forms of statements IF-THEN IF-THEN-ELSE IF-THEN-ELSIF Sequence of statements is executed only if the condition evaluates to TRUE If condition evaluates to FALSE or NULL, it does nothing In either case control passes to next statement after the IF-THEN structure IF <condition> THEN statements; END IF; Sequence of statements in the ELSE clause is executed only if the condition evaluates to FALSE or NULL IF <condition> THEN statements; ELSE statements; END IF; -------------------------------------------------------------------------------declare /*Simple if condition */ x number; begin x := &x; if x >= 35 then dbms_output.put_line('Passed'); else dbms_output.put_line('Failed'); end if; end; ----------------------------------------------------IF-THEN-ELSIF Structure This construct allows selection of action from several mutually exclusive alternatives The IF statement can have any number of ELSIF clauses The final ELSE is optional Conditions are evaluated one by one from top to bottom Syntax IF <condition1> THEN statements; ELSIF <condition2> THEN statements; ELSIF <condition3> THEN 8

statements; ELSE statements; END IF; Example 1 Declare y number; /*Multiple ifs */ Begin y := &y; if y >= 70 then dbms_output.put_line('Distinction'); elsif y >= 60 then dbms_output.put_line('First class'); elsif y >= 50 then dbms_output.put_line('Second class'); elsif y >= 35 then dbms_output.put_line('Passed'); else dbms_output.put_line('Failed'); end if; end; Example 2 create table adm (Name varchar2(30), Marks number(3), College varchar2(30), Fees number(5)); /*Use of multiple if's Accept name and marks from user. Depending upon marks entered the college and fees should be decided and the record should be entered in the adm table.*/ Declare n adm.name%type; m adm.marks%type; c adm.college%type; f adm.fees%type; Begin n := '&n'; m := &m;

if m >= 95 then c := 'COEP'; f := 10000; elsif m >= 90 then c := 'MIT'; f := 15000; elsif m >= 85 then c := 'VIT'; f := 22000; elsif m >= 80 then c := 'D Y Patil'; f := 27000; elsif m >= 75 then c := 'Pune Vidyarthi'; f := 33000; else dbms_output.put_line('Cannot get admission'); end if; if c is not null and f is not null then dbms_output.put_line('Your College is '|| c || ' and fees are ' || f); Insert into adm values(n,m,c,f); commit; end if; end;

10

Nested IF declare x number; y number; z number; begin x := 1; y := 6; z := 7; if x >= 1 then if y >= 100 then dbms_output.put_line('A'); else dbms_output.put_line('B'); end if; else if z >= 50 then dbms_output.put_line('C'); else dbms_output.put_line('D'); end if; end if; end; /

11

Nested Blocks
declare x number; begin x := 80; dbms_output.put_line(abc); declare y number; begin y := 90; dbms_output.put_line('Inner Block variable value ' || y); end; dbms_output.put_line('Outer Block variable value ' || x); end; Scope of variables A variable declared in the outer block is accessible in the inner block. But a variable declared in the inner block is accessible only in the inner block. declare outer number; begin outer := 80; declare inner number; begin inner := 90; dbms_output.put_line('Inner Block variable value ' || inner); dbms_output.put_line('Outer block variable is accessible in the inner block); dbms_output.put_line('Outer block variable value ' || outer); end; dbms_output.put_line('Outer Block variable value ' || outer); dbms_output.put_line('Inner Block variable value ' || inner); end; /

12

Labels If the variables names of the outer and inner blocks are same then labels have to be used within the inner block to avoid ambiguity. <<outer_block>> declare x number; begin declare x number := 100; begin dbms_output.put_line('Value of the inner block x is ' || x); -- Giving value of x of the inner block to the outer block x outer_block.x := x; end; x := x + 500; dbms_output.put_line('Value of the outer block x is ' || x); end; /

13

LOOPS The ability to repeat or skip sections of a block can be achieved with the usage of LOOP or GOTO statements There are three forms of the LOOP statement LOOP WHILE-LOOP FOR-LOOP LOOP Statement LOOP repeats a sequence of statements Statements to be repeated are placed between keyword LOOP and END LOOP With each iteration of the loop, the sequence of statements is executed, then control resumes at the top of the loop LOOP statements; END LOOP; EXIT Statement Used to complete a loop if further processing in a loop is undesirable or impossible There are two forms of the EXIT statement EXIT EXIT-WHEN Forces a loop to complete unconditionally Must be placed inside a loop LOOP statements; IF <condition> THEN EXIT; -- exit loop immediately END IF; END LOOP; -- control resumes here Example of Loop /*To show 1 to 10 on screen*/ Declare x number; Begin x := 1; Loop dbms_output.put_line(x); x := x + 1; exit when x > 10; End Loop;

14

dbms_output.put_line(end); End; create table five (no number); /*Inserting multiples of five in table five*/ Declare x number; Begin x := 5; Loop Insert into five values(x); x := x + 5; exit when x > 50; End Loop; End; Buffer Size enhancement in Oracle 10g The below program would throw error till 9i R2; since there was buffer size limitation. declare x number:= 1; begin loop dbms_output.put_line(x); x := x + 1; Exit when x >= 303; end loop; end; Now from 10g the buffer size is unlimited (till 10 000 000 byte)

15

WHILE-LOOP Statement Associates a condition with a sequence of statements enclosed within LOOP-END LOOP Condition evaluated before each iteration If condition evaluates to TRUE, sequence of statements is executed and control resumes at the top of the loop If condition evaluates to FALSE or NULL, loop is bypassed and control passes to next statement Number of iterations depends on the condition and is unknown until the loop completes WHILE <condition> LOOP statements; END LOOP; Example 1 of while loop to show 1 to 15 declare x number; Begin x := 1; while x <=15 Loop dbms_output.put_line(x); x := x + 1; End Loop; end; Example 2 Forces a loop to complete unconditionally declare z number; /*Using break after z reaches to 8*/ Begin z := 1; while z <=15 Loop dbms_output.put_line(z); z := z + 1; exit when z = 8; End Loop; end;

16

While Loop v/s Basic Loop While Loop declare x number; Begin x := 1; while x > 15 Loop dbms_output.put_line(x); x := x + 1; End Loop; dbms_output.put_line('End of program'); end; / The loop will never get executed since the condition is wrong from the start of the iteration. Basic Loop Declare x number; Begin x := 1; Loop dbms_output.put_line(x); exit when x = 1; x := x + 1; End Loop; dbms_output.put_line('End of program'); End; / The loop gets executed at least once. FOR LOOP Advantages -1) No need of declaring loop variable 2) No need of giving condition 3) No need of updation statement (increment or decrement ) 4)Code becomes small and compact Disadvantage -Updation can be done by only one.

17

Syntax FOR <counter> IN [REVERSE] lower_bound .. higher_bound LOOP statements; END LOOP Example 1 of for loop /*To show 1 to 10 on screen*/ begin for x in 1..10 Loop dbms_output.put_line(x); End Loop; end;

Example 2 /*Reverse for loop 10,9,8 1*/ Begin for i in REVERSE 1 ..10 Loop dbms_output.put_line(i); End Loop; end;

Example 3 Calculating compound interest for a principal of Rs.100 @10% for each year. Values will be shown of the CI after each year. create table CI_100 (year number(2), total number(4)); ---------------------------------Declare p number := 100; tot number; /*Calculation of compound interest. Rs.100 is principal. Rate of interest is 10%.

18

Period is 5 years. */ Begin for y in 1..5 Loop /* Tot variable is getting 10% more than p */ tot := p + p * 0.10; Insert into CI_100 values(y,tot); /*Since the next interest is based on the current interest so the tot will be considered as p for the next year*/ p := tot; End Loop; end;

19

Nested Loops
create table discount_details (quarter number(2), month number(2), discount varchar2(5));

Expected Output QUARTER 1 1 1 2 2 2 3 3 3 4 4 4 MONTH 1 2 3 4 5 6 7 8 9 10 11 12 DISCOUNT 12% 11% 10% 9% 8% 7% 6% 5% 4% 3% 2% 1%

12 rows selected.

20

declare q number; m number; d number; dis varchar2(10); c number; begin q := 1; m := 0; d := 12; loop exit when q > 4; c := 0; loop exit when c >= 3; m := m + 1; dis := d || '%'; insert into discount_details values(q,m,dis); d := d - 1; c := c + 1; end loop; q := q + 1; end loop; end;

21

GOTO Statement Branches to a label unconditionally When executed, it changes the flow of control in a PL/SQL block Two parts needed to code a GOTO statement Define a label name Use the GOTO statement to pass control to the label Label name optionally used to name a PL/SQL block or statements within the block Defined using angular brackets (<< >>) <<if_fare_label>> IF condition THEN statements; END IF; statements; GOTO if_fare_label; Transfer of control using GOTO statement is allowed in the following places from a block to an executable statement branch from an exception handler into an enclosing block Transfer of control using GOTO statement is NOT allowed in the following places from within one IF statement or loop clause to another from an enclosing block into a sub-block from an exception handler into the current block out of a subprogram to keywords Examples of GOTO 1) create table prec (name varchar2(20), dept varchar2(20)); create table mahrec (name varchar2(20), dept varchar2(20), city varchar2(30));

22

/*Pune records should go in both the tables prec and mahrec. Any other city record should go in mahrec table only.*/ declare n prec.name%type; d prec.dept%type; c mahrec.city%type; begin n := '&n'; d := '&d'; c := '&c'; if c = 'Pune' then goto s1; else goto s2; end if; <<s1>> Insert into prec values(n,d); <<s2>> Insert into mahrec values(n,d,c); end;

Legal and Illegal Usage of GOTO Ex 1 declare x number; begin x := 90; <<s1>> dbms_output.put_line('S1'); if x = 90 then dbms_output.put_line('Condition is true'); goto s1; else dbms_output.put_line('Condition is false'); end if; end;

23

/ Ex 2 declare x number := 90; begin if x = 90 then goto abc; else <<abc>> dbms_output.put_line('Failed'); end if; end; / Ex 3 declare <<abc>> x number := 9; begin goto abc; end; / Ex 4 Cannot jump from the parent block into a child block. Begin dbms_output.put_line('a'); goto x1; begin <<x1>> dbms_output.put_line('x1'); end; dbms_output.put_line('b'); end;

24

Ex 5 Can jump from the child block into the parent block provided the label is after the child block. Begin dbms_output.put_line('a'); begin goto x1; end; dbms_output.put_line('b'); <<x1>> dbms_output.put_line('x1'); end; / Ex 6 Cannot jump from the child block into the parent block if the label is above the child block. Begin dbms_output.put_line('a'); <<x1>> dbms_output.put_line('x1'); begin goto x1; end; dbms_output.put_line('b'); end; / Ex 7 Can jump outside the loop. declare x number := 1; begin loop dbms_output.put_line(x); x := x + 1; exit when x >= 10; if x = 5 then goto label1; end if; end loop; <<label1>> dbms_output.put_line('Came out of loop!');

25

end; Ex 7 If the label is defined inside the loop then from outside the loop cannot jump inside the loop. declare x number := 1; begin goto label1; loop dbms_output.put_line(x); x := x + 1; exit when x >= 10; <<label1>> dbms_output.put_line('Came inside the loop!'); end loop; end; Ex 8 Cannot jump from parent loop into the child loop; begin for a in 1 .. 5 loop dbms_output.put_line(a); if a = 3 then goto label1; end if; for b in 1 .. 4 Loop dbms_output.put_line(b); <<label1>> dbms_output.put_line('XYZ'); End Loop; end loop; end; /

26

Topic No. 2 PL/SQL Records

Index

Objects of type RECORD are called PL/SQL records PL/SQL records have uniquely named fields, which can belong to different datatypes Define a RECORD type TYPE <typename> IS RECORD (fieldname1 <fieldtype> : fieldnameN <fieldtype> ; (%TYPE and %ROWTYPE can be used to specify <fieldtype>] Example 1 of Record Type Declare TYPE empdetails IS RECORD (eno Emp.Empno%type, name Emp.Ename%type, s Emp.Sal%type); VE empdetails; Begin Select empno,ename,sal Into VE from Emp where ename = 'SMITH'; dbms_output.put_line(VE.eno || ' - ' || VE.name || '-' ||VE.s); End; Example 2 of Record Type Declare TYPE si IS RECORD (p number, n number, r number := 4.5); /* r variable of si type has been given value. */ VSI si; x number; Begin VSI.p := 5000; VSI.n := 6; x := (VSI.p * VSI.n * VSI.r) / 100; 27

dbms_output.put_line(x); End; Using a record type in another record type. Declare type Address_details is record (sector char(3), colony varchar2(50), bldg_name varchar2(25), pincode number(7)); type personal_details is record (name varchar2(60), Addr Address_Details, age number); V personal_details; Begin V.name := 'John'; V.Addr.sector := 'S1'; V.Addr.colony := 'Model'; V.Addr.bldg_name := 'Hill View'; V.Addr.pincode := 6775; dbms_output.put_line('The building name is ' || V.Addr.bldg_name); dbms_output.put_line('The pincode is ' ||V.Addr.pincode); End; -------------------------------------

28

%Rowtype with Record declare type t1 is record (ed emp%rowtype, dd dept%rowtype); VT1 t1; Begin select * into VT1.ed from emp where ename = 'KING'; select * into VT1.dd from dept where dname = 'ACCOUNTING'; dbms_output.put_line(VT1.ed.ename); dbms_output.put_line(VT1.dd.dname); end; / Assigning Values of one record to another. declare type t1 is record (eno Emp.Empno%type, name Emp.Ename%type, s Emp.Sal%type); v1 t1; v2 t1; begin v1.eno := 3; v1.name := 'ds'; v1.s := 998; v2 := v1; dbms_output.put_line (v2.name); end;

29

Topic No. 3 Cursors

Index

To process a SQL statement, PL/SQL opens a work area called a context area. PL/SQL uses this area to execute SQL statements and store processing information A PL/SQL construct called Cursor allows you to name a context area, access its information and in some cases, control its processing Explicit Cursors Defined by the user to keep track of which row is being processed, when a query returns multiple rows Defining a Cursor A cursor is defined in the declarative part of the PL/SQL block by naming it and associating it with a query CURSOR <cursorname> IS <SELECT statement>; Example CURSOR emp_cur IS SELECT empno, ename, job, sal FROM emp; A Cursor can be manipulated using OPEN FETCH CLOSE Cursor must be declared before it can be referenced using the OPEN, CLOSE or FETCH statements The OPEN Statement Initializes or opens a cursor Cursor must be opened before any rows are returned by the query OPEN <cursorname> Example -OPEN emp_cur; The FETCH Statement Can be executed repeatedly until all rows have been retrieved FETCH <cursorname> INTO var1, , varN; OR FETCH <cursorname> INTO record_variable; Example FETCH emp_cur INTO mrec;

30

The CLOSE Statement Closes the cursor and makes the active set undefined CLOSE <cursorname>; Example CLOSE emp_cur; Once a cursor is closed, it can be reopened by using the OPEN statement Attributes of Explicit Cursors Every cursor has four attributes that can be used to access the cursors context area %NOTFOUND %FOUND %ROWCOUNT %ISOPEN To use these attributes, simple append them to the name of the cursor %NOTFOUND evaluates to TRUE if last FETCH failed because no more rows were available evaluates to FALSE if last FETCH returned a row %FOUND evaluates to TRUE if last FETCH returned a row evaluates to FALSE if last FETCH failed because no more rows were available %ROWCOUNT returns the number of rows FETCHed from the active set so far %ISOPEN evaluates to TRUE if an explicit cursor is open evaluates to FALSE if an explicit cursor is closed

31

Cursor Mechanism: 1) Demo of First Fetch and then Exit create table cur1 (a int); insert into cur1 values(1); commit; declare cursor cf is select * from cur1; M cf%rowtype; flag boolean; begin open cf; loop Fetch cf into M; flag := cf%notfound; if flag = true then dbms_output.put_line('cf%notfound is true'); else dbms_output.put_line('cf%notfound is false'); end if; Exit when cf%notfound; dbms_output.put_line(M.a); end loop; end; /

2) Demo of First Exit and then Fetch declare cursor cf is select * from cur1; M cf%rowtype; flag boolean; begin open cf; loop Exit when cf%notfound; flag := cf%notfound; if flag = true then dbms_output.put_line('cf%notfound is true'); else dbms_output.put_line('cf%notfound is false'); 32

end if; Fetch cf into M; dbms_output.put_line(M.a); end loop; end; /

3) Demo of Row Count declare cursor cf is select * from emp; M cf%rowtype; x number; begin open cf; x := cf%rowcount; dbms_output.put_line(x); fetch cf into M; x := cf%rowcount; dbms_output.put_line(x); fetch cf into M; x := cf%rowcount; dbms_output.put_line(x); fetch cf into M; x := cf%rowcount; dbms_output.put_line(x); close cf; open cf; x := cf%rowcount; dbms_output.put_line(x); fetch cf into M; x := cf%rowcount; dbms_output.put_line(x); end;

33

Examples of Cursor 1) To transfer names and sal of employees from emp table where sal >= 2500 in table try1 create table try1 (no number, ename varchar2(50), sal number); Declare Cursor cf is select ename,sal from emp where sal >= 2500; M cf%rowtype; N number; Begin Open cf; N := 0; Loop Fetch cf into M; Exit when cf%notfound; N := cf%rowcount; Insert into try1 values(N,M.ename,M.sal); End Loop; Close cf; End; 2) Use of %FOUND attribute Accepting the job from user and finally showing how many such jobs are there. Declare Cursor cf is select * from emp where job = '&J'; M cf%rowtype; N number; J Emp.Job%type;

34

Begin Open cf; N := 0; Loop Fetch cf into M; Exit when cf%notfound; If cf%found then N := N + 1; End if; End Loop; Close cf; If N > 0 then dbms_output.put_line('Total number of job ' || J || ' is '|| N); else dbms_output.put_line('No such job'); End If; End; ---------------------------------------------------------------------3) Use of IsOpen attribute Declare Cursor cf is select ename, deptno from emp where deptno = 20; M cf%rowtype; /*The cursor is not opened before Loop. So using IsOpen attribute to open the cursor if it is not open.*/ Begin /* Cursor is not opened!!! */ Loop If cf%IsOpen then Fetch cf into M; else Open cf; dbms_output.put_line('Cursor is now opened'); End if; exit when cf%notfound; dbms_output.put_line(M.ename ||'--' || M.deptno); End Loop; End; --------------------------------------------------------------------------------------

35

4) Transferring the first five records from emp table into another table FirstFive create table firstfive as select empno,ename,sal,deptno from emp where 1=2; Declare Cursor cf is Select * from emp; M cf%rowtype; N number; Begin Open cf; N := 1; while N <= 5 Loop Fetch cf into M; Insert into firstfive values(M.empno,M.ename,M.sal,M.deptno); N := N +1; End Loop; End; 5) Displaying the 3rd record entered in the table emp Declare Cursor cf is select * from emp; M cf%rowtype; Begin Open cf; Loop fetch cf into M; if cf%rowcount = 3 then dbms_output.put_line(M.empno||'-'||M.ename||'-'||M.sal||'-'||M.deptno); EXIT; end if; exit when cf%notfound; End Loop; End;

36

6) To see the first person( or first record entered) who has got salary > 2800 Declare Cursor cf is select * from emp where sal > 2800; M cf%rowtype; Begin Open cf; Loop fetch cf into M; if cf%rowcount = 1 then dbms_output.put_line(M.empno||'-'||M.ename||'-'||M.sal||'-'||M.deptno); exit; end if; End Loop; End; Cursor FOR Loop Implicitly declares its loop index as a record of %ROWTYPE, Implicitly opens the cursor Repeatedly fetches rows of values from the active set into fields in the record Implicitly closes the cursor when all rows have been processed or the loop is exited The statements in the loop construct are executed once for each row that satisfies the query associated with the cursor name Cursor FOR loop is used to simplify coding No need of -1)Open cursor 2)Fetch 3)Exit 4)Close cursor 7) To show records where salary is > 3000 Declare Cursor cf is select * from emp where sal >= 3000; Begin For mrec in cf Loop dbms_output.put_line(mrec.ename||' '||mrec.sal||' '||mrec.deptno); End Loop; End; 37

For Loops using sub queries No need of declaring cursor. A private cursor within an anonymous block can be created. To show names of employees who have job MANAGER. begin for MREC in (select * from emp) Loop if MREC.job = 'MANAGER' then dbms_output.put_line('Name is ' ||MREC.ename); end if; END LOOP; end;

38

Parameterized Cursor The same cursor can be reopened and closed with different active sets. declare cursor cf(pjob emp.job%type) is select empno,ename,job,sal from emp where job = pjob; M cf%rowtype; begin open cf('ANALYST'); LOOP FETCH CF INTO M; EXIT WHEN CF%NOTFOUND; dbms_output.put_line('Name of ' || M.job || ' is ' || M.ename); end loop; close cf; open cf('CLERK'); LOOP FETCH CF INTO M; EXIT WHEN CF%NOTFOUND; dbms_output.put_line('Name of ' || M.job || ' is ' || M.ename); end loop; close cf; open cf('MANAGER'); LOOP FETCH CF INTO M; EXIT WHEN CF%NOTFOUND; dbms_output.put_line('Name of ' || M.job || ' is ' || M.ename); end loop; close cf; END; /

39

Cursor FOR UPDATE OF and CURRENT OF CURRENT OF clause is used in an UPDATE or DELETE statement to refer to the current row of the cursor The cursor must be declared with the FOR UPDATE OF clause and must be open and positioned on a row If the cursor is not open, CURRENT OF clause results in an error 8) Example of Cursor FOR UPDATE OF and CURRENT OF create table esal (empno number, sal number); insert into esal values(1,16000); insert into esal values(2,14000); insert into esal values(3,8000); insert into esal values(4,6500); insert into esal values(5,9000); insert into esal values(6,11000); insert into esal values(7,5500); insert into esal values(8,3500); insert into esal values(9,2200); insert into esal values(10,7000);

40

Multiple updations depending on the salary clause in one pl/sql block Declare Cursor cf is select * from esal

For Update of sal;


M cf%rowtype; Begin Open cf; Loop Fetch cf into M; exit when cf%notfound; If M.Sal >= 16000 Then M.Sal := 20000; ElsIf M.Sal >= 14000 Then M.Sal := 18200; ElsIf M.Sal >= 12000 Then M.Sal := 16700; ElsIf M.Sal >= 10000 Then M.Sal := 13500; ElsIf M.Sal >= 8000 Then M.Sal := 11000; ElsIf M.Sal >= 6000 Then M.Sal := 9500; ElsIf M.Sal >= 4000 Then M.Sal := 7500; Else M.Sal := 5000; End If; Update esal set sal = M.Sal

Where Current Of cf;


End Loop; 41

End;

Implicit Cursors Automatically defined and opened, by Oracle, to process each SQL statement most recently opened context area is referred to as a SQL% cursor

Attributes of Implicit Cursors Although OPEN, CLOSE and FETCH statements cannot be used to manipulate the SQL% cursor, the attributes can be used to access its context area Attributes evaluate to NULL, before the cursor is opened automatically The following four cursor attributes can be used to access the SQL% cursors context area SQL%NOTFOUND SQL%FOUND SQL%ROWCOUNT SQL%ISOPEN SQL%NOTFOUND evaluates to TRUE if an INSERT, UPDATE or DELETE statement affected no rows, else it evaluates to FALSE SQL%FOUND logical opposite of SQL%NOTFOUND evaluates to TRUE if an INSERT, UPDATE or DELETE affected one or more rows, else it evaluates to FALSE SQL%ROWCOUNT returns the number of rows affected by an INSERT, UPDATE or DELETE statement SQL%ISOPEN Oracle automatically closes an implicit cursor after executing its associated SQL statement For an implicit cursor SQL%ISOPEN always evaluates to FALSE 9) Example of Implicit Cursors Begin

42

Delete from emp where ename = '&name'; If SQL%Found Then dbms_output.put_line('Record found and it is deleted'); End If; If SQL%NotFound Then dbms_output.put_line('No record is present of the given name.'); End If; End;

10) Implicit Cursor for rowcount Declare C number := 0; Begin Update Emp set sal = sal + 500 where deptno = &deptno; /*If no record is updated since the deptno supplied is wrong then giving the customised error message.*/ If SQL%Rowcount = 0 then dbms_output.put_line('No records are updated since the department number entered is not in the table.'); End if; /*To prevent sal to be updated where deptno is > 3 */ If SQL%RowCount > 3 then Rollback; dbms_output.put_line('Cannot update since there are more than 3 deptnos'); End If; If SQL%RowCount Between 1 and 3 then c := SQL%RowCount; dbms_output.put_line(c || ' records updated.'); End If; End;

43

Topic No.4 PL/SQL Tables

Index
Features of PL/SQL tables are as follows 1) It is a composite data type. 2) They are modeled as similar to database tables, but they are not permanent tables. So they can be created and manipulated only in a PL SQL block. 3) They can have only one column but any data type 4) It will have a primary key which is compulsory for the reference of values 5) There is no name to the column and primary key 6) The data type of the primary key is BINARY_INTEGER. BINARY_INTEGER is a special data type which can be given only to the column of PL/SQL table for its indexing purpose to store and retrieve values. Range of binary_integer is -2147483647 to + 2147483647 7) Size is unconstrained (Table size grows as the rows are added to the table). 8) Can visualize a Pl/SQL table as a single dimensional vertical array, which can hold unlimited elements. 9) Suitable for storing and displaying the values of one column of a table given by a cursor. Example of PL SQL Table Each name from the emp table is given to the vname plsql table by using cursor. Then those names from vname table are displayed . Declare Type nametable IS TABLE OF CHAR(10) INDEX BY BINARY_INTEGER; /*Creating variable vname of nametable type.*/ vname nametable; Cursor cf is select ename from emp; i number; vrows number; /*i is for the loop and vrows is for displaying the total names from the vname table*/ Begin Open cf; i := 1; Loop Fetch cf into vname(i); /*Transferring each ename into vname table*/ Exit when cf%NotFound;

44

vrows := cf%rowcount; i := i + 1; End Loop; Close cf; /*Now retrieving the names from the vname plsql table using for loop.*/ For n in 1..vrows Loop dbms_output.put_line('Name is '||vname(n)); End Loop; End;

Properties of a PL SQL table -- Exists Count First Last Next Prior Delete declare Type discount is TABLE OF number INDEX By Binary_Integer; d discount; Begin d(5) := 90; d(2) := 50; d(8) := 70; d(11) := 67; d(14) := 68; d(1) := 1; d(23) := 5; d(23) := 51; dbms_output.put_line('The value at 23 index number is ' || d(23)); dbms_output.put_line('The value at index number 6 is ' || d(6)); if d.EXISTS(6) Then dbms_output.put_line(d(6)); else dbms_output.put_line('There is no element in the sixth row');

45

end if; dbms_output.put_line('The total number of elements in d are '|| d.count); /*dbms_output.put_line('The first index number is ' || d.FIRST); dbms_output.put_line('The last index number is ' || d.LAST); dbms_output.put_line('The index number after 2 is ' || d.next(2)); dbms_output.put_line('The index number before 8 is ' || d.prior(8)); d.delete(5); dbms_output.put_line('The total number of elements in d are '|| d.count); d.delete(11,14); dbms_output.put_line('The total number of elements in d are '|| d.count); d.delete; dbms_output.put_line('The total number of elements in d are '|| d.count); */ end; Table made up of %Rowtype Declare Type nametable IS TABLE OF EMP%ROWTYPE INDEX BY BINARY_INTEGER; /*Creating variable vname of nametable type.*/ vname nametable; Cursor cf is select * from emp; i number; vrows number; /*i is for the loop and vrows is for displaying the total names from the vname table*/ Begin Open cf; i := 1; Loop Fetch cf into vname(i); /*Transferring each ename into vname table*/ Exit when cf%NotFound; vrows := cf%rowcount; i := i + 1; End Loop; Close cf; /*Now retrieving the names from the vname plsql table using for loop.*/ For n in 1..vrows

46

Loop dbms_output.put_line('Name is '||vname(n).ENAME); End Loop; End; / PLSQL Table made of a Record type declare

r is record (a number, b number); type t is table of r index by binary_integer;


type v t; v1 r; begin v1.a := 89; v1.b := 9; v(1) := v1; dbms_output.put_line(v(1).a); end; or declare type r is record (a number, b number); type t is table of r index by binary_integer; v t; begin v(1).a := 8; dbms_output.put_line(v(1).a); end; /

47

Topic No. 6 Bulk Binding

Index

This article begins a discussion of how to work with collections. Previous versions of Oracle had limitations for collection usage in regards to SQL statement processing. Bulk Bind and Collect features were introduced to reduce the SQL processing overhead by efficient use of collections in PL/SQL code. The PL/SQL engine executes procedural statements and sends all SQL statements present in the code to the SQL engine. The SQL engine will parse and execute the query or DML statement and return the expected output back to the PL/SQL engine. This switch between the two engines is called context switching. We mostly concentrate on the SQL statement to tune performance issues. It is worth noting that excessive context switching can affect performance. This would be substantially significant when we are carrying out SQL statements in a loop. The features discussed below were introduced to reduce this overhead of SQL processing. Introduced in Oracle 8i, these features are being improved on with every new release. Two PL/SQL features, Bulk Bind and Bulk collect help in improving performance and utilizing resources effectively from within PL/SQL code. These features reduce context switching, (i.e., multiple SQL statements being executed from the code resulting in calls to SQL engine), and carry out the operation on the concerned object at one go. Since the SQL statements are fired in a bundle, frequent access to SQL engine is reduced. In cases where the PL/SQL code is being executed on a different terminal than the server itself, this would also result in optimal network usage rather than too frequent network calls to access the SQL engine. Bulk Collects (Reading data in bulk) The bulk collect option instructs the SQL engine to bulk bind the output collections before returning them to the PL/SQL engine. This allows us to load data dynamically into collections at one shot for further processing. Bulk collect can be used with SELECT INTO, FETCH INTO and RETURNING INTO statements. Syntax:

... bulk collect into collection...


For example, let us assume that we need to load all pending transactions into a temporary table and process them one by one. As part of validation, there is a need to refer to the data in the same table, from time to time, for each transaction 48

being processed. One possible method to write the code would be to load all of the data in the temporary table to a collection type. This way, additional queries on the table could be avoided (context switch) and the processing could be carried out in PL/SQL itself. This idea is further improved on by the use of the bulk collect option, as all data is loaded into PL/SQL at the same time.

Examples of Bulk
Bulk used with Select into clause
declare type emp_details is table of emp.ename%type index by binary_integer; V emp_details; begin

select ename bulk collect into V from emp;


for i in V.first .. V.last loop dbms_output.put_line(V(i)); end loop; end;

Bulk used in Cursors declare cursor cf is select * from emp; type emp_tab is table of emp%rowtype index by binary_integer; V emp_tab; begin open cf; fetch cf bulk collect into V; for j in V.first .. V.last loop dbms_output.put_line(V(j).ename); end loop; end;
To restrict the bulk collect to certain number of rows the limit clause is to be used. declare cursor cf is select * from emp; type emp_tab is table of emp%rowtype index by binary_integer; V emp_tab; first_4 int; begin open cf; first_4 := 4; fetch cf bulk collect into V limit first_4-- The first 4 rows are given for j in V.first .. V.last

Limit Clause

49

loop end loop; end;

dbms_output.put_line(V(j).ename);

Example of processing first 4 rows, after that processing the next set of 4 rows. declare cursor cf is select * from emp; type emp_tab is table of emp%rowtype index by binary_integer; V emp_tab; first_4 int; next_4 int; begin open cf; first_4 := 4; fetch cf bulk collect into V limit first_4; for j in V.first .. V.last loop dbms_output.put_line('First 4 names -- ' ||V(j).ename); end loop; next_4 := 4; fetch cf bulk collect into V limit next_4; for j in V.first .. V.last loop dbms_output.put_line('Next 4 names -- ' ||V(j).ename); end loop; end; /

Bulk with DML Bulk Delete declare type emp_tab is table of emp%rowtype index by binary_integer; V emp_tab; begin delete from emp returning empno,ename,job,mgr,hiredate,sal,comm,deptno bulk collect into V; for i in V.first .. v.last loop dbms_output.put_line(V(i).ename); end loop; end;

50

Topic No. 7 EXCEPTIONS


Introduction to Exceptions

Index

An error condition is called an Exception When an error occurs, an exception is raised i.e. normal execution stops and control transfers to the exception handling part of the PL/SQL block or subprogram To handle raised exceptions, separate routines called exception handlers are written There are two types of exceptions Pre-defined exceptions (Internal Exceptions) User-defined exceptions You cannot declare an exception twice in the same block, but can declare the same exception in two different blocks Exceptions declared in a block are local to that block and global to all its subblocks Enclosing blocks cannot reference exceptions declared in a sub-block because blocks can only reference local or global exceptions Predefined Exceptions Are implicitly raised whenever a PL/SQL block violates an Oracle rule or exceeds a system-dependent limit Every Oracle error has a number, but exceptions must be handled by name PL/SQL predefines some common Oracle errors as exceptions These predefined exceptions are declared globally by PL/SQL Some Pre-defined Exceptions CURSOR_ALREADY_OPEN NO_DATA_FOUND TOO_MANY_ROWS VALUE_ERROR ZERO_DIVIDE More than one exception can be handled in a single exception handler by separating them with the keyword OR EXCEPTION WHEN NO_DATA_FOUND OR TOO_MANY_ROWS THEN statements; WHEN OTHERS THEN statements; END;

51

Examples of Exception handling 1) NO_DATA_FOUND error (Variable is not having any value.) declare n emp.ename%type; s emp.sal%type; begin select sal into s from emp where ename = '&n'; dbms_output.put_line('Salary is '|| s); /* Exception When NO_DATA_FOUND then dbms_output.put_line('No record'); */ end; 2) TOO_MANY_ROWS error (Variable is having more than one value) declare s emp.sal%type; begin select sal into s from emp; dbms_output.put_line('The salary is '|| s ); Exception When TOO_MANY_ROWS then dbms_output.put_line('Variable can hold only one value at a time'); dbms_output.put_line('Please specify the name of person for getting the salary'); end; 3) ZERO_DIVIDE error (A number divided by zero) declare x number; y number; z number; begin x := &x; y := &y; z := x/y; dbms_output.put_line('The answer is ' || z);

52

Exception When ZERO_DIVIDE then dbms_output.put_line('Cannot divide by zero!!!'); end; 4) DUP_VAL_ON_INDEX error (When a duplicate value is entered in a column having Unique constraint) declare e emp.empno%type; begin e := &e; insert into emp (empno ) values(e); dbms_output.put_line('Successful'); Exception When DUP_VAL_ON_INDEX then dbms_output.put_line('Value already exists'); end; 5) VALUE_ERROR (Error in conversion of string to number) declare n number; begin n := '&n'; dbms_output.put_line(n); Exception When VALUE_ERROR then dbms_output.put_line('Please enter number only'); end; 6) OTHERS (If no error handler works then at least OTHERS will work) declare x number; y number; z number; begin x := &x; y := &y; z := x/y; dbms_output.put_line('The answer is ' || z);

53

Exception When too_many_rows then dbms_output.put_line('More than one value'); When no_data_found then dbms_output.put_line('No value'); /*When OTHERS then dbms_output.put_line('Some run time error has occurred'); dbms_output.put_line('Please execute the program again with proper values.'); rollback;*/ end;

THE CASE_NOT_FOUND EXCEPTION A subtle difference between the CASE construct and the corresponding IF construct occurs when the ELSE leg is omitted. With the IF consruct, if none of the legs is selected then there is no action. But with the CASE construct, if none of the legs is selected then the case_not_found exception (ORA-06592: CASE not found while executing CASE statement ) is raised

declare x number := 4; begin case when x = 1 then dbms_output.put_line('One'); when x = 2 then dbms_output.put_line('Two'); end case; end; / Throws run-time error declare x number := 4; begin case when x = 1 then dbms_output.put_line('One'); when x = 2 then dbms_output.put_line('Two'); end case; exception When case_not_found then dbms_output.put_line('Case not found'); end; /

54

Pragma Exception create table dept1(deptno number primary key, dname varchar2(10)); create table emp1(empno number, ename varchar2(10), deptno number references dept1(deptno)); insert into dept1 values(10,'Acc'); insert into emp1 values(1,'abc',10);

PRAGMA PRAGMA EXCEPTION_INIT tells the compiler to associate an exception name with an Oracle error number. That allows you to refer to any internal exception by name and to write a specific handler for it. declare referential_integrity EXCEPTION; PRAGMA EXCEPTION_INIT( referential_integrity, -2292); begin Delete from dept1 where deptno = &deptno; commit; exception when referential_integrity then dbms_output.put_line('The record cannot be deleted, because related record found in emp1 table'); end; Oracle Error Number for the Constraints violations: Primary Key and Unique is -00001 Foreign Key is -2291, Not Null is -1400 Check is -2290 SQLCODE AND SQLERRM SQLCODE Returns the numeric value for the error code. SQLERRM Returns the message associated with the error number. create table error_log(error_number number, error_message varchar2(255)); declare s emp.sal%type; 55

v_error_code number; v_error_message varchar2(255); begin select sal into s from emp; exception when others then v_error_code := SQLCODE; v_error_message := SQLERRM; Insert into error_log values(v_error_code, v_error_message); commit; end;

Exception Handlers in nested block to continue after run time error comes declare loan_amt number; no_of_months number; installment_rate number; roi number; tot_amt number; begin loan_amt := &loan_amt; no_of_months := & no_of_months; begin installment_rate := loan_amt / no_of_months; exception when zero_divide then no_of_months := 3; installment_rate := loan_amt / no_of_months; end; /* In any case the last 3 lines will get executed */ roi := installment_rate * 0.2; -- 20% roi tot_amt := roi + loan_amt; dbms_output.put_line('The total amount to be paid is '|| tot_amt); end;

56

User-defined Exceptions User-defined exceptions need to be defined in the declarative part of a PL/SQL block, subprogram or database trigger Declared by naming the exception and defining it as datatype EXCEPTION Example DECLARE past_due EXCEPTION; zero_error EXCEPTION; Like variables, user-defined exceptions must be given names Unlike variables, user-defined exceptions cannot be assigned values and cannot be used in SQL statements They need to be raised explicitly using the RAISE statement A block should RAISE an exception only when an error makes it impossible or impractical to finish processing RAISE statement for a given expression can be coded anywhere within the scope of that expression IF mrec.ss_fare <= 0 THEN RAISE zero_error; END IF; An exception raised inside a handler immediately propagates to the enclosing block, which is searched to find a handler for the newly raised exception From there on, the exception propagates normally To re-raise an exception place a RAISE statement in its local handler

57

Example of Exception variable using Raise key word declare p number; n number := 6; si number; r number := 10.5; EX exception; Begin p := &p; if p < 100 then raise EX; else si := (p * n * r) / 100; dbms_output.put_line('The Simple Interest is '|| si); end if; Exception When EX then dbms_output.put_line('The principle amt should be greater than or equal to 100.'); end; -------------------------------------------------------------------------------------RAISE_application_error This can be used to create user defined error message, which can be more descriptive than named exceptions. Syntax - : Raise_application_error(error number,error message); where error number is any parameter between -20,000 and -20,999.Error message is text that is associated with this error. The message parameter must be less than 512 characters. Example of Raise_application_error declare maths number; Begin maths := &maths; if maths < 35 then raise_application_error(-20001,'Failed'); else dbms_output.put_line('Passed'); end if; end;

58

Topic No. 8 PROCEDURES

Index

Advantages of Subprograms Provide Extensibility PL/SQL language can be tailored to suit the needs of the application Promote reusability and maintainability Once validated, they can be used with confidence in any number of applications Simplifies maintenance/enhancement, as subprogram is only affected if definition changes Provide Modularity Program can be broken down into manageable, well-defined logical modules Supports top-down design and stepwise refinement approach to problem solving Aid in abstraction Allow mental separation from particulars Stubs allow programmers to defer definition of procedures/functions until main program is tested and debugged

Procedure performs specific action Stored in database and can be invoked or called by any anonymous block Can take parameters Datatype specifier in parameter declaration must be unconstrained

Has two parts Specification begins with keyword PROCEDURE, ends with procedure name or parameter list Body begins with keyword IS, ends with keyword END followed by optional procedure name CREATE [OR REPLACE] PROCEDURE <procedurename> [(parameter1, parameterN)] IS [local declarations] BEGIN executable statements; [EXCEPTION exception handlers] END [<procedurename>]; 59

parameter stands for variablename [IN|OUT|IN OUT] datatype [{:= | DEFAULT} value] When a procedure is created, Oracle automatically performs these steps Compiles the procedure Stores the compiled code Stores the procedure in the database The PL/SQL compiler used to compile the code If an error occurs, the procedure is created but it is invalid Enforce Data Security Can grant users access to a procedure that queries a table, but not grant access to the table itself Improve Database Performance Less amount of information sent over network Compilation not required to execute code Procedure present in shared pool so retrieval from disk not required Memory Savings Only one copy of procedure needs to be loaded in memory for execution by multiple users Increase in Development Productivity Can avoid redundant coding and increase productivity, by writing a single procedure Integrity Procedures need to be tested only once, to guarantee that it returns accurate results Calling a Stored Procedure Can call a procedure in a PL/SQL statement Example branch_sum(NYK); Can call a procedure from SQL*Plus Example SQL> EXECUTE branch_sum(NYK);

60

Examples of Procedures 1) --Procedure without parameters create or replace procedure pr1 as s emp.sal%type; Begin select sal into s from emp where ename = 'SMITH'; dbms_output.put_line(s); end; Parameter Modes for Procedures and Functions Used to define the behavior of formal parameters Can be used with any subprogram Three parameter modes IN (Default) OUT IN OUT IN allows values to be passed to the subprogram being called inside the subprogram it acts like a constant actual corresponding parameter can be a constant, literal, initialized variable or expression can be initialized to default values 2) --Supplying parameters to a procedure which are by default of IN type create or replace procedure pr2 (En IN Emp.Empno%type, Name IN Emp.ename %type, S Emp.Sal%type) is Begin Insert into Emp(empno,ename,sal) Values(En,Name,S); dbms_output.put_line('One record inserted through procedure'); End;

61

3) Giving default values to the parameters Due to default value given the parameter becomes optional also. But if any other value is given then it takes it. a) Default Value for the last parameter (s). create or replace procedure pr3 (Eno emp.empno%type, N emp.ename%type, S emp.sal %type, dno emp.deptno%type DEFAULT 10) is Begin Insert into emp (empno,ename,sal,deptno) values(Eno,N,S,dno); dbms_output.put_line('Record inserted'); End; -- While executing --exec pr3 (1,'o',800) -----> (No deptno parameter given!!!) b) Default value for the other than last parameter: create or replace procedure dept_rec(p_deptno in number, p_dname in dept.dname %type default 'Accounts', p_loc dept.loc%type) is begin Insert into dept values(p_deptno, p_dname, p_loc); dbms_output.put_line('Done'); end; To exceute such a parameter we have to use the named exec dept_rec(p_loc => 'Pune', p_deptno => 10); Mutiple ways of passing the parameters -1. Positional Method -- exec dept_rec(20,'Purchase','Mumbai'); 2. Named Method a. exec dept_rec(p_loc => 'Bangalore',p_deptno => 30); b. exec dept_rec(p_deptno =>20, p_dname => 'Inspection', p_loc => 'Pune'); c. exec dept_rec(p_dname =>'Stores', p_loc => 'Mumbai', p_deptno => 10); 3. Combination Method 62

notation

After positional there can be named parameters, but after named there cannot be positional parameters. a. exec dept_rec(10, p_loc =>'Mumbai', p_dname =>'Marketing'); b. exec dept_rec(p_deptno =>20, 'Pune','Research'); -- Wrong 3) --Cannot give size to the parameters 4) create or replace procedure pr4 (name char, marks number) is Begin if marks >= 35 then dbms_output.put_line('Passed'); else dbms_output.put_line('Failed'); end if; dbms_output.put_line(name); End; OUT parameter allows values to be returned to the caller of a subprogram inside the subprogram it acts like an uninitialized variable actual corresponding parameter must be a variable; it cannot be a constant or expression its value cannot be assigned to another variable or reassigned to itself 5) create or replace procedure pr5(Name IN varchar2, Salary OUT number) Is Begin Select sal into Salary from emp where ename = Name; End; --Steps for displaying the OUT parameter --1) Compiling the procedure. --2) Declare the bind variable on SQL prompt as variable payment number -- Bind variables are of SQL* plus environment which are used to hold the return --value given by the procedure or function. --3)Now execute the proc -- exec pr5('SMITH', :payment) --4)To display the value of payment -- print payment --5)Bind variables are session specific.Their existence is removed as the session --ends.

63

Using local variable for out parameter when procedure is called inside another block. create or replace procedure p1(n in emp.ename%type, s out emp.sal%type) is begin select sal into s from emp where ename = n; end; declare x emp.sal%type; begin p1('SMITH',x); dbms_output.put_line(x); end; 6) IN OUT parameter allows initial values to be passed and returns updated values to the caller inside the subprogram it acts like an initialized variable actual corresponding parameter must be a variable; it cannot be a constant or expression can be assigned a value and its value can be assigned to another variable a) create or replace procedure pr6(x IN OUT number) Is Begin x := (x * x); End; /*pr6 procedure cannot be executed independently on sql prompt. It has to be called inside a plsql block. It actually gives the square value to the variable of that plsql block. In short IN OUT type of paramter makes a procedure similar to function, as the function also returns the value to the calling environment.*/ 64

b)

declare a number; Begin a := &a; pr6(a); /*When a is given as a parameter , it's status is of IN OUT. So IN means the user input value and OUT means the changes square figure due to the procedure pr6. After the procedure is called with a as parameter then a value gets changed. At this time a acts as a OUT parameter, since the procedure is giving the changed value to a.*/ dbms_output.put_line(a); End;

65

7) IN OUT example from with respect to database a) create or replace procedure salrise(salary IN OUT number) is Begin salary := salary + (salary * 0.20); End; /*Salrise procedure will increment the sal by 20% and give the value to the calling plsql block.*/ b) Declare n emp.ename%type; s emp.sal%type; Begin n := '&n'; select sal into s from emp where ename = n; dbms_output.put_line('The old salary is ' || s); /*Now calling the procdure Salrise and giving s as a IN parameter*/ Salrise(s); /*After giving the salary as a parameter the salary value gets incremented by 20% */ dbms_output.put_line('The changed salary is '|| s); /*updating the table*/ Update emp set sal = s where ename = n; dbms_output.put_line('Salary of ' || n || ' is updated in the table.'); Exception When NO_DATA_FOUND then dbms_output.put_line('No such name'); end; -------------------------------------------------------------------------------------------------IN, OUT, IN OUT Modes Behavior with respect to the local variables A procedure is having IN mode of parameter. create or replace procedure p1(a in number) is begin dbms_output.put_line('The value of a inside the procedure is '||a); end p1;

66

declare x number; begin x := 3; dbms_output.put_line('The value of x before sending to the procedure is ' || x); p1(x); dbms_output.put_line('The value of x after coming outside is '|| x); end; Output:

The IN mode value is accessible inside the procedure as well as the value still exists after coming out of the procedure. Only the procedure cannot change the value. A procedure is having OUT mode of parameter. create or replace procedure p1(a out number) is begin dbms_output.put_line('The value of a inside the procedure is '||a); end p1; declare x number; begin x := 3; dbms_output.put_line('The value of x before sending to the procedure is ' || x); p1(x); dbms_output.put_line('The value of x after coming outside is '|| x); end; Output

A procedure is having out mode of parameter.

67

If an initialized local variable is sent as a parameter then its original value is not accessible inside the procedure. Also after coming outside the procedure the original value is lost. A procedure is having IN OUT mode of parameter. create or replace procedure p1(a in out number) is begin dbms_output.put_line('The value of a inside the procedure is '||a); a := a + 7; -- Value can get changed end p1; declare x number; begin x := 3; dbms_output.put_line('The value of x before sending to the procedure is ' || x); p1(x); dbms_output.put_line('The value of x after coming outside is '|| x); end;

A procedure is having in out mode of parameter. If an initialized local variable is sent as a parameter then its original value is accessible inside the procedure. (Also it can get changed by the procedure) After coming outside the procedure the original or changed value is retained by that variable.

68

Declaring Sub procedures Create table log_table(username varchar2(30), log_date date); Create or replace procedure delete_emp(p_ename emp.ename%type) Is /*Creating a sub procedure */ Procedure log_emp is Begin Insert into log_table Values(user, SYSDATE); End; Begin Delete from emp where ename = p_ename; log_emp; End;

69

Handled or Unhandled Exceptions for a procedure getting called in another. create or replace procedure innerproc(x number,y number) is begin dbms_output.put_line(x/y); exception when zero_divide then dbms_output.put_line('y cannot be negative'); end; create or replace procedure outerproc as begin dbms_output.put_line('AA'); /*Calling the innerproc precedure.*/ innerproc(4,0); dbms_output.put_line('cc); exception when others then dbms_output.put_line('A Run Time Error has occurred.'); end; If the innerproc procedure is not having any error handler then the error handler outer procedure gets executed. AUTHID CURRENT_USER If a procedure reads data from table and performs some DML then AUTHID CURRENT_USER is applicable. Due to this clause the owner of the procedure only can execute this procedure and read or perform DML. Even if the owner has given execute privilege of such a procedure to any other user, still the other user cannot execute the procedure. So from data security point of view this clause is helpful. The following scenario will explain this concept. Case study ---Log on as System/Manager (DBA Login) create user a1 identified by a1; grant resource, create session to a1; ------------------------------------------------------create user a2 identified by a2;

70

grant resource, create session to a2; ---------------------------------------------------------Without AUTHID CURRENTUSER conn a1/a1 create table t1(a number); insert into t1 values(1); commit; ----------------------------------------------------------create or replace procedure t1_data is x number; begin select a into x from t1; dbms_output.put_line(x); end; ---------------------------------------------grant execute on t1_data to a2; ------------------------------------------------conn a2/a2 exec a1.t1_data --- Data is shown. With AUTHID CURRENTUSER conn a1/a1 create or replace procedure t1_data1 AUTHID CURRENT_USER is x number; begin select a into x from t1; dbms_output.put_line(x); end; ---------------------------------------grant execute on t1_data1 to a2; ------------------------------------------conn a2/a2 exec a1.t1_data1 -- Data is not shown. Throws error.

71

Autonomous Transaction
Autonomous Transaction is a feature of oracle 8i which maintains the state of its transactions and save it , to affect with the commit or rollback of the surrounding transactions. With PRAGMA AUTONOMOUS_TRANSACTION , the transaction state is maintained independently . Commit/Rollback of nested transaction will have no effect on the other transaction. It is advisable to increase the value of TRANSACTIONS parameter in the INIT parameter file to allow for the extra concurrent transaction .

Autonomous Transactions provide a new method of controlling transactions in stored procedures. Autonomous Transactions allow you to create a new sub transaction that may commit or rollback changes independent of the parent transaction.

Scenario One: Without Autonomous Transaction


Creating the test table t CREATE TABLE t (test_value VARCHAR2(50)); Creating the procedure child_block CREATE OR REPLACE PROCEDURE child_block IS BEGIN INSERT INTO t VALUES ('Child block insert'); COMMIT; END child_block; Creating the procedure parent_block CREATE OR REPLACE PROCEDURE parent_block IS BEGIN INSERT INTO t VALUES ('Parent block insert'); child_block; ROLLBACK; END parent_block; Run the parent procedure exec parent_block; Check the results

72

SELECT * FROM t; TEST_VALUE -------------------------------------------------Parent block insert Child block insert The commit inside the child_block has committed the entire transaction So there is no effect of rollback given by the parent_block truncate table t;

Scenario Two: With Autonomous Transaction


CREATE OR REPLACE PROCEDURE child_block IS PRAGMA AUTONOMOUS_TRANSACTION; BEGIN INSERT INTO t VALUES ('Child block insert'); COMMIT; END child_block; Run the parent procedure exec parent_block; Check the results SELECT * FROM t; TEST_VALUE -------------------------------------------------Child block insert Due to autonomous transaction the transaction of the child_block was only committed and not the transaction of parent_block.

73

Topic No. 9 Functions


Index

Subprogram that returns a value Have a RETURN clause Stored in database and can be invoked or called by any anonymous block Generally take parameters Datatype specifier in parameter declaration must be unconstrained Has two parts Specification begins with keyword FUNCTION, ends with RETURN clause Body begins with keyword IS, ends with keyword END followed by optional function name

Syntax CREATE [OR REPLACE] FUNCTION <functionname> [(argument1, argumentN)] RETURN datatype IS [local declarations] BEGIN executable statements; [EXCEPTION exception handlers] END [<functionname>]; argument stands for variablename [IN|OUT|IN OUT] datatype [{:= | DEFAULT} value] Format CREATE FUNCTION day_fn(mday NUMBER) RETURN CHAR IS disp_day CHAR(15); BEGIN executable statements; RETURN disp_day; EXCEPTION statements; END day_fn; Calling a Function Can call a function as a PL/SQL statement Example chardays := day_fn(3); 74

Can call a function as part of an expression Example IF day_fn(3) = TUESDAY THEN statements; END IF; The RETURN Statement Immediately completes execution of subprogram and returns control to caller In procedures cannot contain an expression returns control to the caller before normal end of procedure is reached In functions must contain an expression, evaluated when it is executed Examples of Functions 1) a) create or replace function eo ( n number) RETURN char is disp char(30); Begin If Mod(n,2) = 0 Then disp := 'Number is even'; else disp := 'Number is odd'; End If; dbms_output.put_line(disp); RETURN disp; End; Select eo(9) from dual; OR

75

Declare x number; y char(30); Begin x := &x; /*Calling the function eo, supplying the parameter x and giving the value to y*/ y := eo(x); end; 2) Database example a) create or replace function Remarks ( x number) RETURN char is disp char(30); Begin If x >= 70 Then disp := 'Distinction'; Elsif x >= 60 Then disp := 'First Class'; Elsif x >= 50 Then disp := 'Second Class'; Elsif x >= 35 Then disp := 'Passed'; Else disp := 'Failed'; End If; RETURN disp; End; c) Using this function to insert values for a column of a table create table st (name varchar2(10),

76

marks number, result char(30)); Directly calling the function inside Insert statement Insert into st values(John, 90, Remarks(90)); -- Directly calling the function in the values clause b) Calling a function in select statement create or replace function raise_sal(s number) return number is begin return s + 8000; end; Select ename,deptno,job,sal, raise_sal(sal) From emp; create or replace function f1(s number) return number is begin delete from emp; return 0; end; Wrong usage of functions select ename,sal,f1(sal) from emp; Function having select statement in its body can get called in the select statement of the same table or any different table. create or replace function highest return number is x number; begin select max(sal) into x from emp; return x; end; select ename, sal, highest, highest - sal "Diff" from emp; Applicable for displaying aggregate values alongwith non groupbale columns.

77

3) Returning more than one value using OUT parameter a) create or replace function getdetails(no number,name OUT varchar2) RETURN varchar2 as vloc varchar2(30); begin select dname,loc into name,vloc from dept where deptno = no; RETURN vloc; End; b) -- First declare two bind variables location and deptname --SQL> variable deptname varchar2(100) (size is imp) --SQL> variable location varchar2(100) Begin :location := getdetails(30, :deptname); End; -- To see both the values -- print deptname location -------------------------------------------------------------------------

78

Topic No. 10 Packages

Index

Database objects that group logically related PL/SQL types, objects and subprograms They cannot be called, passed parameters to or nested There are two parts Specification Body Advantages of Packages Modularity allows encapsulation of related types, objects and subprograms in a named PL/SQL module easy to understand with simple, clear and well defined interfaces helps in application development Easier Application Design when designing applications, only interface information in package specifications initially required can code and compile specification without body stored subprograms referencing the package can be compiled as well need not define package bodies until ready to complete the application Information Hiding can specify which types, items, and subprograms are public or private definition of private subprograms is hidden so that only the package (not the application) is affected if the definition changes simplifies maintenance and enhancement and protects integrity of the package Better performance when a packaged subprogram is called for the first time, the whole package is loaded into memory later calls to related subprograms in the package require no disk I/O Package Specification Is an interface to the applications Declares the types, variables, constants, exceptions, cursors and subprograms available for use Holds public declarations, visible to the application Can be thought of as an operational interface Scope of the declarations are local to the database schema and global to the package Lists the package resources available to applications Created using CREATE PACKAGE command

79

Syntax for Package Specification CREATE [OR REPLACE] PACKAGE <packagename> AS Global variables declaration; Procedure specifications; Function specifications; Type Definitions; Cursor Declarations END [<packagename>]; Package Body Implements the package specification Fully defines cursors and subprograms Holds implementation details and private declarations, hidden from the application Can be thought of as a black body Can be replaced, enhanced or replaced without changing the interface Can be changed without recompiling calling programs Scope of the declarations are local to the package body Declared types and objects are inaccessible except from within the package body Initialization part of a package is run only once, the first time the package is referenced Syntax for Package Body CREATE [OR REPLACE] PACKAGE BODY <packagename> AS Private members (variables and procedure/functions/cursors/types) Procedure Code; Function Code; Implementation of Types; Use of Cursors; Using Global variables in the members of the package. END [<packagename>]; Referencing Package Objects Packaged objects and subprograms must be referenced using the dot notation packagename.typename packagename.objectname packagename.subprogramname

80

E.g - DBMS_OUTPUT.PUT_LINE

Maintaining a Package Can drop a package using the DROP command DROP PACKAGE <packagename> Example DROP PACKAGE airlines; To drop just one construct, remove it from the package and then recompile the package

Examples of Packages 1) Creating a package of 3 procedures Package Specification create or replace package pk1 is procedure x(a number); procedure y(b number); procedure z(c number); end PK1; Package Body create or replace package body pk1 is procedure x(a number) is Begin dbms_output.put_line('Procedure p1'); End x; procedure y(b number) is Begin dbms_output.put_line('Procedure p2'); End y; /*Suppose in the package body if all the procedures are not written then it will give error.*/ /*procedure z(c number) is Begin 81

dbms_output.put_line('Procedure p3'); End z; */ End pk1; ------------------------------Using the Package pk1SQL > Execute PK1.X(4);

2) Use of global variable in a function and procedure Package Specification create or replace package pk2 as g number; function m(a number) return number; procedure n; end pk2; Package Body create or replace package body pk2 as function m(a number) return number is Begin g := a; return g; End m; procedure n is Begin if g >= 100 then dbms_output.put_line('Discount is 20%'); else dbms_output.put_line('Discount is 10%'); end if; end n; End pk2; Using the package in a PL/SQL block Declare x number; Begin 82

x := pk2.m(700); pk2.n; End;

83

3) Use of Type in a Procedure Package Specification create or replace package pk3 as Type t1 is RECORD (e1 Emp.Empno %Type, e2 Emp.Ename%Type, e3 Emp.Sal%Type); Procedure p1; end pk3; Package Body create or replace package body pk3 as procedure p1 is v t1; /*Using the type of the package directly inside the procedure.*/ Begin select empno,ename,sal into v from emp where ename = 'SMITH'; dbms_output.put_line(v.e1 || '-' || v.e2 || '-' || v.e3); End; End pk3;

4) Use of Cursor in Procedure Package Specification create or replace package pk4 as cursor cf is select * from emp where job = 'MANAGER'; m cf%rowtype; procedure CP; End pk4;

84

Package Body create or replace package body pk4 as procedure CP is Begin Open cf; Loop fetch cf into m; /*Showing the first entry of manager*/ if cf%rowcount = 1 then dbms_output.put_line(m.empno || '-' || m.ename || '-' || m.sal); else exit; end if; End Loop; Close cf; End CP; End pk4;
5) Example of a body less package (Persistent state of variables)

create or replace package bodyless is x number := 200; y number := 100; /*Variables needs to be initialized if it is in a body less package*/ end; begin bodyless.x := bodyless.x + 100; dbms_output.put_line('Now the value of the x variable of bodyless package is ' || bodyless.x); bodyless.y := bodyless.y + 500; dbms_output.put_line('Now the value of the y variable of bodyless package is ' || bodyless.y); end; /* Note-- Execute the block 3-4 times, you will see that the values of x and y are getting changed. But disconnect or close the session and start the session again. The original values of the x and y from the package gets initialized. */ 85

6) Example of private members in package body

create or replace package prvmembers is procedure m1; end; create or replace package body prvmembers is /*Creating Private Procedure in the body*/ procedure m2 is begin dbms_output.put_line('This is the private member of the package body'); end m2; procedure m1 is begin m2; end; end;
Example of forward declaration of a private member

create or replace package body prvmembers is procedure m2; procedure m1 is begin m2; end; /* Forward declaration of m2 */

/*Creating Private Procedure in the body*/ procedure m2 is begin dbms_output.put_line('This is the private member of the package body'); end m2; end;

86

If the private member is not getting called in any global members then forward declaration is not required. create or replace package body prvmembers is procedure m1 is begin dbms_output.put_line('abc'); end; /*Creating Private Procedure in the body*/ procedure m2 is begin dbms_output.put_line('This is the private member of the package body'); end m2; end; /

One time only procedure (Procedure in package body)


Useful for setting remote connectivity for front ends. In the session for the first time any of the members of the package is called then the procedure will be implicitly invoked. Then for the future calls of the members of the same package in the same session the procedure will not get executed.

create or replace package pk1 is procedure p1; procedure p2; end;

87

create or replace package body pk1 is procedure p1 is begin dbms_output.put_line('p1'); end p1; procedure p2 is begin dbms_output.put_line('p2'); end p2;

begin dbms_output.put_line('Welcome to my package');


end pk1; create or replace package pk1 is type namelist is table of emp.ename%type index by binary_integer; v namelist; cursor cf is select ename from emp; M cf%rowtype; procedure first_five; procedure next_five; end;

88

create or replace package body pk1 is i Number; procedure first_five is begin for i in 1..5 loop dbms_output.put_line(v(i)); end loop; end first_five; procedure next_five is begin for i in 6..10 loop dbms_output.put_line(v(i)); end loop; end next_five; begin i := 0; open cf; loop i := i + 1; fetch cf into M; v(i) := M.ename; exit when cf%notfound; end loop; end To initialize the global variable by taking value from table create or replace package pk1 is average_sal number; function calc_sal_deviation(s number) return number; procedure sal_status(s number);

89

end; create or replace package body pk1 is /*function calc_sal_deviation(s number) return number; procedure sal_status(s number);*/ function calc_sal_deviation(s number) return number is begin if average_sal > s then return (average_sal - s); else return 0; end if; end; procedure sal_status(s number) is begin if average_sal > s then dbms_output.put_line('On Lower Side'); else dbms_output.put_line('On Higher Side'); end if; end; begin select avg(sal) into average_sal from emp; end; exec pk1.SAL_STATUS(800); exec pk1.SAL_STATUS(4000); select sal, pk1.CALC_SAL_DEVIATION(sal) "Deviation" from emp;

90

Example of Overloaded Members create or replace package pk1 is procedure p(a number, b number); procedure p(x varchar2,y varchar2); procedure p(z number); procedure p; end pk1; create or replace package body pk1 is procedure p(a number, b number) is c number; begin c := a + b; dbms_output.put_line(c); end p; procedure p(x varchar2, y varchar2) is begin dbms_output.put_line(x ||y); end p; procedure p(z number) is begin if z > 0 then dbms_output.put_line('The number is positive'); else dbms_output.put_line('The number is negative'); end if; end p; procedure p is begin dbms_output.put_line('No parameter is passed'); end p; end pk1;

91

Example of overloaded members Parameter data types can be same, but names should be different. create or replace package pk2 as procedure p(salary number, commission number); procedure p(salary number, tax number); end pk2; create or replace package body pk2 as procedure p(salary number, commission number) is total number; begin total := salary + commission; dbms_output.put_line(total); end; procedure p(salary number, tax number) is take_home number; begin take_home := salary - tax; dbms_output.put_line(take_home); end; end pk2;

While executing such procedure the parameters need to be called by named method only.
exec pk2.p(salary => 4000, commission => 500); exec pk2.p(salary => 4000, tax=> 200);

Function Overloading:

Create Or Replace Package Pk1 Is Function F (A Number, B Number) Return Number; Function F (A Number, C Number) Return Varchar2; Function F (A Number, D Number) Return Boolean; End; Create Or Replace Package Body Pk1 Is Function F (A Number, B Number) Return Number Is Begin If A >B Then 92

Return 100; Else Return 50; End If; End; Function F (A Number, C Number) Return Varchar2 Is Begin If A >C Then Return 'A Is Greater Than B'; Else Return 'B Is Greater Than A'; End If; End; Function F(A Number, D Number) Return Boolean Is Begin If A >D Then Return True; Else Return False; End If; End; End;

93

Example of Persistent Cursors create or replace package pc is cursor cf is select * from emp; procedure pp1; procedure pp2; c cf%rowtype; end ; create or replace package body pc is procedure pp1 is begin open cf; loop fetch cf into c; dbms_output.put_line(c.empno||'-'||c.ename||'-'||c.sal); exit when cf%rowcount >= 3; end loop; end pp1; procedure pp2 is begin loop fetch cf into c; dbms_output.put_line(c.empno||'-'||c.ename||'-'||c.sal); exit when cf%rowcount >= 6; end loop; close cf; end pp2; end;

94

Function returning an array


create or replace package emp_pack is type emp_details is table of emp%rowtype index by binary_integer; function get_details return emp_details; end emp_pack; create or replace package body emp_pack is function get_details return emp_details is V emp_details; n integer := 0; begin for j in (select * from emp) loop n := n +1; V(n) := j; end loop; return V; end get_details; end emp_pack;

declare X emp_pack.emp_details; begin X := emp_pack.get_details; for i in X.first .. X.last loop dbms_output.put_line(X(i).ename || ' - ' || X(i).sal); end loop; end; /

95

PURITY LEVELS PRAGMA RESTRICT_REFERENCES WNDS - Write No Database State RNDS Read No Database State

WNPS - Write No Package State RNPS - Read No Package State


Example of WNDS and RNDS
create table pt1(a number); Insert into pt1 values(1); Package Specification Code create or replace package pragma_package as function getTotal(commission in number) return number; PRAGMA RESTRICT_REFERENCES (getTotal, WNDS, RNDS); end;

96

Package Body Code -

create or replace package body pragma_package as function getTotal(commission in number) return number is salary number := 0; begin select a into salary from pt1 where a = 1; -- Throws error return (commission + salary); end; end;
PLS-00452: Subprogram 'GETTOTAL' violates its associated pragma

Example of WNPS Package Code

create or replace package purity_levels as pi number := 3.14; function circle_area(radius number) return number; PRAGMA RESTRICT_REFERENCES (circle_area, WNPS); end;
Package Body code -

create or replace package body purity_levels as function circle_area(radius number) return number is area number; begin pi := pi + 5.4; -- Throws error area := pi *radius *radius;

97

return area; end; end; Example of RNPS


Package Specification Code

create or replace package purity_level1 as selling_price number := 4000; tax number := 50; procedure cust_details; PRAGMA RESTRICT_REFERENCES (cust_details, RNPS); end;
Package Body Code --

create or replace package body purity_level1 as procedure cust_details is begin Insert into pt1 values(purity_level1.selling_price); end; end; -----------------------------

98

NOCOPY Compiler Hint Suppose a subprogram declares an IN parameter, an OUT parameter, and an IN OUT parameter. When you call the subprogram, the IN parameter is passed by reference. That is, a pointer to the IN actual parameter is passed to the corresponding formal parameter. So, both parameters reference the same memory location, which holds the value of the actual parameter. By default, the OUT and IN OUT parameters are passed by value. That is, the value of the IN OUT actual parameter is copied into the corresponding formal parameter. Then, if the subprogram exits normally, the values assigned to the OUT and IN OUT formal parameters are copied into the corresponding actual parameters. When the parameters hold large data structures such as collections, records, and instances of object types, all this copying slows down execution and uses up memory. To prevent that, you can specify the NOCOPY hint, which allows the PL/SQL compiler to pass OUT and IN OUT parameters by reference. Case 1: Without the NOCOPY hint create or replace package pack1 is type nametab is table of varchar2(50) index by binary_integer; procedure p1(a in out nametab); end pack1; create or replace package body pack1 is procedure p1(a in out nametab) is begin for i in 1..a.count loop dbms_output.put_line(a(i)); end loop; end p1; end pack1;

99

Implementation: declare i pack1.nametab; c number:=1; begin for x in (select ename from emp) loop i(c) := x.ename; c := c + 1; end loop; pack1.p1(i); /* Here since the NOCOPY is not mentioned all the values are copied into the procedure. This will slow down and performance wise not good */ end;

Case 2: With NOCOPY hint create or replace package pack1 is type nametab is table of varchar2(50) index by binary_integer; procedure p1(a in out NOCOPY nametab); end pack1; create or replace package body pack1 is procedure p1(a in out NOCOPY nametab) is begin for i in 1..a.count loop dbms_output.put_line(a(i)); end loop; end p1; end pack1;

declare i pack1.nametab;

100

c number:=1; begin for x in (select ename from emp) loop i(c) := x.ename; c := c + 1; end loop; pack1.p1(i); /* Here due to the NOCOPY compiler hint the array is again not copied into the procedure but the reference of that array is given. This is good for performance. */ end;

101

Topic No.11 TRIGGERS Index


It is a stored PL/SQL program unit associated with a specific database table Oracle executes (fires) the trigger automatically whenever a given SQL operation affects the table They are invoked implicitly They are useful for customizing a database They should be used only when necessary Can automatically generate derived column values Can prevent invalid transactions Can enforce complex security authorizations Can enforce referential integrity across nodes in a distributed database Can enforce complex business rules Can provide transparent event logging Can provide sophisticated auditing Can maintain synchronous table replicates Can gather statistics on table access Can derive column values automatically Can restrict DML operations to regular business hours

Syntax -CREATE [OR REPLACE] TRIGGER <triggername> BEFORE|AFTER INSERT|DELETE|UPDATE OF <columnnames> ON <tablename> [FOR EACH ROW] WHEN (<condition>) <PL/SQL Block> Name in the ON clause identifies the database table associated with the trigger The trigger event specifies the SQL DML statement (INSERT, DELETE or UPDATE) that affects the table AFTER specifies that the trigger fires after the manipulation is done BEFORE specifies that the trigger fires before the manipulation is done

102

By default, a trigger fires once per table FOR EACH ROW specifies that the trigger fires once per row For the trigger to fire, the Boolean expression in the WHEN clause must evaluate to TRUE REPLACE can be added to the CREATE statement to drop and recreate the trigger automatically CREATE TRIGGER flight_update AFTER INSERT ON reservation FOR EACH ROW BEGIN IF :new.class = F THEN statements; ELSIF :new.class = B THEN statements; END IF; END; Prefix :new is a correlation name that refers to the newly updated column value Within a trigger, the :new and :old values of changing rows can be referenced A single trigger can handle more than one operation Use conditional predicates to identify the type of statement used to invoke the section of code

Examples of Triggers Row Level AFTER clause


-- Main table create table temp as select * from emp; -- Table to transfer inserted record create table instemp
103

as select ename,sal from emp where 1=2; Whenever a row is inserted in the temp table then that new row should be transferred in the instemp table Create or replace trigger trig1 After INSERT on temp For Each Row Begin Insert into InsTemp Values(:new.ename, :new.sal); Dbms_output.put_line('Record inserted'); End; Table to transfer deleted record create table deltemp as select ename,sal from emp where 1=2; --Whenever a row is deleted from temp table then that row should be transferred --in Deltemp table Create or replace trigger Trig2 After DELETE on Temp For Each Row Begin Insert into Deltemp Values(:old.ename, :old.sal); End; Table to transfer the old record before updations create table salary_revision (empid integer,
104

Oldsalary number, Newsalary number); Whenever a record is updated from the temp table then the previous and the changed salary of that employee should go in table Salary_Revision create or replace trigger trig3 After UPDATE on temp For Each Row Begin Insert into Salary_Revision Values(:old.empno, :old.sal, :new.sal); End; Table Level vs Column level update trigger
Drop table update_tab; create table update_tab (a int, b int, c int); create or replace trigger trig_all_columns after update on update_tab for each row begin dbms_output.put_line('Table is updated'); end; insert into update_tab values(1,2,3); update update_tab set a = 7; update update_tab set b = 9; Drop trigger trig_all_columns; create or replace trigger trig_specific_column after update of a on update_tab for each row begin 105

dbms_output.put_line('A column got updated'); end; update update_tab set b = 79; update update_tab set a = 9; Additional example of column level update trigger:

If a salary is updated then it should be always greater than the old salary. create or replace trigger check_sal before update of sal on temp for each row begin if :new.sal < :old.sal then raise_application_error(-20009,'Not possible'); end if; end; Whenever a new record is inserted then the cost value should be always greater than all the existing cost values in the table. Use Of Before Clause When a new record is inserted then it should have sal >= 5000 Create or replace trigger trig7 Before INSERT on temp For Each Row Begin If :new.sal < 5000 then raise_application_error(-20009,'New record sal should be above 5000'); End If; End;

106

To prevent user from deleting records of sal >= 3500 Create or replace trigger trig4 Before DELETE on temp For Each Row Begin if :old.sal >= 3500 Then raise_application_error(-20005,'Cannot delete records of sal greater than or equal to 3500'); End if; End;

Involving a local variable in the trigger code. create table cost_chart (year varchar2(20) unique, cost number); create or replace trigger check_cost before insert on cost_chart for each row declare v_cost number; begin select max(cost) into v_cost from cost_chart; if :new.cost < v_cost then raise_application_error(-20010,'Cost cannot be less than the previous cost'); end if; end; Before Vs. After Triggers
Drop Table Mydata; create table Mydata (id integer, amt integer); create or replace trigger Change_Amt_Before Before Insert on mydata 107

for each row begin :new.amt := :new.amt + 1000; end; / Insert Into mydata Values(1,50); select * from mydata; SQL> select * from mydata; ID AMT ---------- ---------1 1050 create or replace trigger Change_Amt_After After Insert on mydata for each row begin :new.amt := :new.amt + 2500; end; / ERROR at line 1: ORA-04084: cannot change NEW values for this trigger type Sequence of Trigger Error and Constraint Error Execution: create table abc1(a int check (a >= 100)); create or replace trigger Ch_abc1 before insert on abc1 for each row begin if :new.a < 100 then raise_application_error(-20002,'Wrong Value'); end if; end; Insert Into abc1 Values(89); -- Trigger Error drop trigger Ch_abc1; create or replace trigger Ch_abc1 after insert on abc1

108

for each row begin if :new.a < 100 then raise_application_error(-20002,'Wrong Value'); end if; end; Insert Into abc1 Values(89); -- Check Constraint Error

Exception handling in triggers


drop table t1; drop table t2; create table t1(a int); create table t2(a int check (a >= 10)); create trigger trig_t1 after insert on t1 for each row begin insert into t2 values(:new.a); end; insert into t1 values(1); select * from t1; Drop trigger trig_t1; create trigger trig_t1 after insert on t1 for each row begin insert into t2 values(:new.a); exception when others then null; end; insert into t1 values(1); select * from t1; select * from t2;

109

Statement OR Table Level Triggers 1) -- To prevent a new record to be inserted in temp table create or replace trigger trig6 Before INSERT on temp Begin raise_application_error(-20006,'Cannot insert a new record'); End;

2) -- To prevent any record to get deleted from temp table create or replace trigger trig8 Before DELETE on temp Begin raise_application_error(-20011,'Cannot delete'); End; 3) -- No transaction should take place on SUNDAY create or replace trigger trig9 Before INSERT or UPDATE or DELETE on temp Begin If to_char(Sysdate,'DAY') = 'SUNDAY' Then raise_application_error(-20015,'No transactions on Sunday'); End If; End; Demo of Number of times the trigger code getting executed in case of a row level and statement level trigger. drop table tr; create table tr(a int);

110

insert into tr values(1); insert into tr values(2); insert into tr values(3); insert into tr values(4); commit; create or replace trigger tr_trig3 after delete on tr begin dbms_output.put_line('Delete statement given.......'); end; / set serverout on delete from tr where a >= 10; delete from tr where a <=2 ; Rollback; create or replace trigger tr_trig4 after delete on tr for each row begin dbms_output.put_line('Seed infotech'); end; / select * from tr; delete from tr where a >= 10; delete from tr where a <= 2; drop table tr;

Demo of sequence of execution of row level and statement level triggers


create table trigtab(a number);

111

create or replace trigger tt1 before insert on trigtab begin dbms_output.put_line('Before statement level'); end; create or replace trigger tt2 before insert on trigtab for each row begin dbms_output.put_line('Before row level'); end; create or replace trigger tt3 after insert on trigtab for each row begin dbms_output.put_line('After row level'); end;

create or replace trigger tt4 after insert on trigtab begin dbms_output.put_line('After Statement level'); end;

112

Multiple Triggers on the Same tables same event with the same mode of execution Drop Table Tab1; CREATE TABLE TAB1(A INT); CREATE OR REPLACE TRIGGER TRIG1_ON_TAB1 AFTER INSERT ON TAB1 FOR EACH ROW BEGIN DBMS_OUTPUT.PUT_LINE('A'); END; / CREATE OR REPLACE TRIGGER TRIG2_ON_TAB1 AFTER INSERT ON TAB1 FOR EACH ROW BEGIN DBMS_OUTPUT.PUT_LINE('B'); END; / CREATE OR REPLACE TRIGGER TRIG3_ON_TAB1 AFTER INSERT ON TAB1 FOR EACH ROW BEGIN DBMS_OUTPUT.PUT_LINE('C'); END; / INSERT INTO TAB1 VALUES(1);

113

Mutating table error


When a row level trigger is based on a table then the trigger body cannot read data from the same table. Also DML on the same table is not possible. create or replace trigger trig11 after delete on temp1 for each row declare x number; begin select sal into x from temp1 where ename = 'SMITH'; dbms_output.put_line(x); end; delete from temp1; create or replace trigger trig11 after delete on temp1 for each row begin insert into temp1 (empno,ename) values(78,'pqr'); end; delete from temp1 where ename = 'KING';

114

For a statement level trigger there will be no error. create or replace trigger trig11 after delete on temp1 declare x number; begin select sal into x from temp1 where ename = 'SMITH'; dbms_output.put_line(x); end; delete from temp1 where ename = 'MARTIN'; create or replace trigger trig11 after delete on temp1 begin insert into temp1 (empno,ename) values(78,'pqr'); end; / delete from temp1 where ename = 'KING';

115

Combining DML events in one Trigger Any DML should be traced in the table KeepTrace create table KeepTrace (username varchar2(30), Operation varchar2(30), Date_of_Operation date, Time_of_operation varchar2(40)); Create or replace trigger trig10 After INSERT or DELETE or UPDATE On Temp For each row Begin If Inserting Then Insert into KeepTrace Values(user, 'Record is Inserted', Sysdate, to_char(sysdate,'hh:mi:ss')); ElsIf Deleting Then Insert into KeepTrace Values(user, 'Record is Deleted',Sysdate, to_char(sysdate,'hh:mi:ss')); ElsIf Updating Then Insert into KeepTrace Values(user,'Record is Updated',Sysdate, to_char(sysdate,'hh:mi:ss')); End If; End;

116

Calling a procedure inside a trigger create table EmpSummary (Total_Salary number); Procedure Code create or replace procedure TotSal is vsal number; Begin delete from EmpSummary; Select sum(sal) into vsal from emp; Insert into EmpSummary Values(vsal); End; create or replace trigger EmpUpdates
After insert or update on emp

CALL TOTSAL Or if the trigger has its code then create or replace trigger EmpUpdates After insert or update on emp Begin -- Trigger Code -- Trigger Code TotSal -- Trigger Code End

117

Instead Of Triggers Provides a transparent way of modifying views that cannot be modified directly through INSERT, UPDATE or DELETE statements because underlying tables contain joins It is called INSTEAD OF because Oracle fires the trigger instead of the triggering statement(s) Users can be transparent to the trigger because they write normal DML statements against the view and the INSTEAD OF trigger is used to take care of the modifications It can be placed on Object Views to insert, update or delete data in the underlying relational tables CREATE TRIGGER emp_insert INSTEAD OF INSERT ON emp_view BEGIN statements; END; Instead of triggers can be used only with views. Effective for joins which are based on equi join To have an cascading effect of update on both the tables if columns are -matching --Step 1 Creating tables s and r; create table s (rollno number, name varchar2(20)); create table r (rollno number, marks number); --Step 2 Inserting records in s and r. insert into s values(1,'a'); insert into s values(2,'b');

118

insert into r values(1,90); insert into r values(2,87); --Step 3 Creating an Equijoin View on s and r Create or replace view SR as select s.rollno,s.name,r.marks from s,r where s.rollno =r.rollno; Example of Instead of Insert Create or replace trigger tig14 Instead Of INSERT on SR For Each Row Begin /*Inserting the new record into both the tables s and r.*/ Insert into s values(:new.rollno, :new.name); Insert into r values(:new.rollno, :new.marks); End;

DDL EVENT TRIGGERS


1) To prevent a Scott from dropping table TT create or replace trigger prevent_drop Before Drop on Scott.Schema Begin If ora_dict_obj_owner = 'SCOTT' and ora_dict_obj_name = 'TT' and ora_dict_obj_type = 'TABLE'

119

Then raise_application_error(-20020,'Cannot drop table TT'); End If; End;

2) To keep a track of Scott new objects The new objects should get added in a table ScottObjects ScottObjects is owned by DBA create table ScottObjects (Object_name varchar2(30), Date_of_Creation date); create or replace trigger Put_New_Objs After CREATE ON Scott.Schema Begin Insert into ScottObjects Values(ora_dict_obj_name,Sysdate); End;

3) To keep a track of Scott's dropped objects. The dropped objects should get added in a table ScottDrop create table ScottDrop (Object_name varchar2(30), Date_of_Creation date); create or replace trigger Put_Drop_Objs After DROP ON Scott.Schema Begin Insert into Scottdrop Values(ora_dict_obj_name,Sysdate); End; 4) Preventing Scott to drop column h2 of table hh Step 1 Login as scott create table hh (h1 number, h2 number, h3 number);

120

Step 2 Log in as sysdba Create or Replace Trigger Prev_Drop_Col Before ALTER on Scott.Schema Begin If ora_dict_obj_name = 'HH' and ora_is_drop_column('H2') Then raise_application_error(-20067,'Cannot drop column h2'); End If; End;

5) --To prevent Scott from modifying the data type of column h3 of hh table Log in as DBA create or replace trigger Prev_Modify Before ALTER on Scott.Schema Begin If ora_dict_obj_name = 'HH' and ora_is_alter_column('H3') Then raise_application_error(-20045,'Cannot modify column H3'); End If; End;

121

Autonomous Transactions with Triggers


Normally in a trigger we cannot provide commit or rollback. But some times when a trigger is doing the auditing work then record of audit table should get committed but the original transaction should get rolled back. Now this is only possible if pragma autonomous_transaction clause is provided in the trigger definition. Example 1 We have to make a trigger, which will ensure that the changed (updated) salary should not exceed thrice of the current (old) salary. Steps 1) Create the audit_table create table audit_table (username varchar2(10), date_of_action date, time_of_action varchar2(20), old_salary number, new_salary number,; 2) create the trigger create or replace trigger bu before update of sal on emp for each row declare

pragma autonomous_transaction;
begin if :new.sal >= :old.sal * 3 then insert into audit_table values(user,sysdate, to_char(sysdate,'hh:mi:ss'), :old.sal, :new.sal,:old.ename);

commit;
raise_application_error(-20001, 'You have tried to do something you should not have and we know it'); end if; end; /

122

Step3 Now try to update Smiths salary. His current salary is 800, so provide the new salary as 3200 update emp set sal = 3200 where ename = 'SMITH'; -- Throws error

select * from audit_table;

123

Topic 12:

Oracle Supplied Packages

124

1) DBMS_SQL
What Is Dynamic SQL?

Index

Dynamic SQL enables you to write programs those reference SQL statements whose full text is not known until runtime. Before discussing dynamic SQL in detail, a clear definition of static SQL may provide a good starting point for understanding dynamic SQL. Static SQL statements do not change from execution to execution. The full text of static SQL statements are known at compilation, which provides the following benefits:

Successful compilation verifies that the SQL statements reference valid database objects. Successful compilation verifies that the necessary privileges are in place to access the database objects. Performance of static SQL is generally better than dynamic SQL.

Because of these advantages, you should use dynamic SQL only if you cannot use static SQL to accomplish your goals, or if using static SQL is cumbersome compared to dynamic SQL. However, static SQL has limitations that can be overcome with dynamic SQL. You may not always know the full text of the SQL statements that must be executed in a PL/SQL procedure. Your program may accept user input that defines the SQL statements to execute, or your program may need to complete some processing work to determine the correct course of action. In such cases, you should use dynamic SQL. For example, a reporting application in a data warehouse environment might not know the exact table name until runtime. These tables might be named according to the starting month and year of the quarter, for example INV_01_1997, INV_04_1997, INV_07_1997, INV_10_1997, INV_01_1998, and so on. You can use dynamic SQL in your reporting application to specify the table name at runtime. You might also want to run a complex query with a user-selectable sort order. Instead of coding the query twice, with different ORDER BY clauses, you can construct the query dynamically to include a specified ORDER BY clause. Dynamic SQL programs can handle changes in data definitions, without the need to recompile. This makes dynamic SQL much more flexible than static SQL. Dynamic SQL lets you write reusable code because the SQL can be easily adapted for different environments. Dynamic SQL also lets you execute data definition language (DDL) statements and other SQL statements that are not supported in purely static SQL programs.

125

Why Use Dynamic SQL? You should use dynamic SQL in cases where static SQL does not support the operation you want to perform, or in cases where you do not know the exact SQL statements that must be executed by a PL/SQL procedure. These SQL statements may depend on user input, or they may depend on processing work done by the program. The following sections describe typical situations where you should use dynamic SQL and typical problems that can be solved by using dynamic SQL Executing DDL and SCL Statements in PL/SQL In PL/SQL, you can only execute the following types of statements using dynamic SQL, rather than static SQL:

Data definition language (DDL) statements, such as CREATE, DROP, GRANT, and
REVOKE

Session control language (SCL) statements, such as ALTER SESSION and SET ROLE

Executing Dynamic Queries You can use dynamic SQL to create applications that execute dynamic queries, whose full text is not known until runtime. Many types of applications need to use dynamic queries, including:

Applications that allow users to input or choose query search or sorting criteria at runtime Applications that allow users to input or choose optimizer hints at run time Applications that query a database where the data definitions of tables are constantly changing Applications that query a database where new tables are created often

126

DBMS_SQL is used to write dynamic SQL in stored procedure and to parse DDL statements. Some Procedures and Functions in DBMS_SQL package are Open_Cursor Opens a new cursor and assigns a cursor ID number. PARSE Parses the DDL or DML statements, i.e. it checks the syntax and associates it with the opened cursor. In case of DDL statements they are executed immediately after parsing. PARSE (ID number of the cursor, DDL or DML statement, language_flag)
language_flag

Determines how Oracle handles the SQL statement. The following options are recognized:
V6 (or 0) specifies version 6 behavior. NATIVE (or 1) specifies normal behavior

for the database to

which the program is connected.


V7

(or 2) specifies Oracle7 behavior.

EXECUTE Executes the SQL statement and returns the number of rows processed. Especially suitable for keeping track of count of rows affected by a DML statement. Examples: create or replace procedure drop_table(p_tablename in varchar2) is C NUMBER; begin c := DBMS_SQL.OPEN_CURSOR; DBMS_SQL.PARSE(C,'DROP TABLE ' || p_tablename, DBMS_SQL.NATIVE); exception when others then dbms_output.put_line('Table name to be dropped does not exist'); end;

127

create or replace procedure delete_rows(table_name varchar2) is c number; rows_deleted number; begin c := DBMS_SQL.OPEN_CURSOR; DBMS_SQL.PARSE(c, 'Delete from ' || table_name, DBMS_SQL.NATIVE); rows_deleted := DBMS_SQL.EXECUTE(c); dbms_output.put_line('The total number of rows deleted are ' || rows_deleted); Exception when others then dbms_output.put_line('No rows deleted'); end;

Execute immediate: The Parsing and the execution is done immediately one after the other.
CREATE PROCEDURE del_rows (p_table_name IN VARCHAR2, p_rows_deld OUT NUMBER) IS BEGIN EXECUTE IMMEDIATE 'delete from '||p_table_name; p_rows_deld := SQL%ROWCOUNT; END;

/
VARIABLE deleted NUMBER EXECUTE del_rows('test_employees',:deleted); PRINT deleted

128

Performing DCL command through triggers.


SQL> create user demo_ddl identified by demo_ddl; User created.

SQL> grant create user to demo_ddl; Grant succeeded. SQL> grant drop user to demo_ddl; Grant succeeded. SQL> connect demo_ddl/demo_ddl Connected.

create table new_users_info (username varchar2(20), password varchar2(20)); create or replace trigger NEW_USERS_TRIG after insert on NEW_USERS for each row declare -- this pragma will allow our trigger to perform DDL pragma autonomous_transaction; begin execute immediate 'Create user ' || :new.username || ' Identified by ' || :new.pw; dbms_output.put_line('User is created'); end; Insert into New_Users values (John,John); Example 3 create table metadata (tname varchar2(20), column1 varchar2(30), dt_col1 varchar2(20), column2 varchar2(30), dt_col2 varchar2(20)); create or replace trigger create_table

129

after insert on metadata for each row declare pragma autonomous_transaction; begin execute immediate 'Create Table ' || :new.tname || '(' || :new.column1 || ' ' || :new.dt_col1 || ','|| :new.column2 || ' ' || :new.dt_col2 || ')' ; end; /

DBA should provide create any table privilege to the current user for having the effect of the above trigger.

Logon as DBA grant create any table to demo_ddl;


Create any table is a system privilege conn demo_ddl/demo_ddl insert into metadata values ('try1','name','varchar2(20)','marks','number'); desc try1

130

Example 4 Trigger for dropping a table create or replace trigger drop_table after delete on metadata for each row declare pragma autonomous_transaction; begin execute immediate 'Drop Table ' || :old.tname; end; /

131

2) DBMS_DDL Package
Scenario of recompilation for a procedure
drop table tx1; create table tx1(a number); insert into tx1 values(1); create or replace procedure px1 is s number; begin select a into s from tx1; end; select status from user_objects where object_name='PX1'; alter table tx1 add b number select status from user_objects where object_name='PX1'; alter procedure px1 compile; select status from user_objects where object_name='PX1'; OR Exec px1;

Index

132

DBMS_DDL.ALTER_COMPILE(object_type, owner name, object_name) Object_type can be a procedure, function, package specification, package body or a trigger only. create or replace procedure compile_procedure(procedure_name varchar2) as begin DBMS_DDL.ALTER_COMPILE('PROCEDURE','SCOTT', procedure_name); dbms_output.put_line('The procedure ' || procedure_name || ' is recompiled'); end; exec compile_procedure('PX1'); select status from user_objects where object_name='PX1';

133

DBMS_JOB Package
Enables the scheduling and execution of PL/SQL programs. Submit is the procedure of DBMS_JOB package. The parameters of Submit procedure are ---

Index

JOB Unique identifier of the job (Out parameter) WHAT PL/SQL code to execute as a job NEXT_DATE Next execution date of the job INTERVAL Date function to compute the next execution date of a job. Example Every next day a new record should get added in the table djt1.

Create table djt1(a number); Create sequence jobseq; Create or replace procedure p1 Is Begin Insert into djt1 Values(jobseq.nextval); End; / Variable jobno number begin dbms_job.submit ( job => :jobno, what => 'P1;', next_date => trunc(sysdate), interval => 'trunc(sysdate + 1)' ); commit; end;

134

In the init.ora file JOB_QUEUE_PROCESSES = n has to be setted n can be between 1 and 36. Change the system date to the next date. The t1 table gets the next value of the sequence. Again repeat the process.

Imp setting

job_queue_processes = n (n between 1 and 36) in the init.ora file

Select job,what from user_jobs; Exec dbms_job.Broken(1,true) 1 is job no Exec dbms_job.Remove(1) Dbms_job.change(jobno, what, next_date, interval) Jobno cannot be changed. If there is no change for what, next_date, interval then specify NULL For having interval of 60 seconds it will be Sysdate + (59/ (24*60*60))

135

3) UTL_FILE Package

Index

UTL_FILE package is used to write data to operating system files such as text files, word files, excel files, etc. The data from the oracle table is transferred to the o.s. files. So we can generate a report of Oracle data in a non-oracle format. Members of UTL_FILE Package are as follows
FOPEN function

This function opens a file for input or output. The file location must be an accessible directory, as defined in the instance's initialization parameter UTL_FILE_DIR. The complete directory path must already exist; it is not created by FOPEN. It takes 3 parameters 1. Directory name The path 2. File name (.txt, .doc,.xls,) 3. Open_Mode a. r Read Text b. w-- Write Text c. a Append Text
PUTF procedure

This procedure is a formatted PUT procedure. It works like a limited printf(). The format string can contain any text, but the character sequences '%s' and '\n' have special meaning.
%s \n

Substitute this sequence with the string value of the next argument in the argument list. Substitute with the appropriate platform-specific line terminator.

FCLOSE procedure

This procedure closes an open file identified by a file handle.


136

NEW_LINE procedure (Can specify how many blank lines are required. Default is 1)

This procedure writes one or more line terminators to the file identified by the input file handle. This procedure is separate from PUT because the line terminator is a platform-specific character or sequence of characters.
Syntax
UTL_FILE.NEW_LINE ( file IN FILE_TYPE, lines IN NATURAL := 3);

Example of generating a text file of salary status from the emp table.
create or replace procedure sal_status (p_filedir in varchar2, p_filename in varchar2) is v_filehandle UTL_FILE.FILE_TYPE; Cursor emp_info is select ename,sal,deptno from emp order by sal desc; Begin v_filehandle := UTL_FILE.FOPEN(p_filedir,p_filename,'w'); UTL_FILE.PUTF(v_filehandle,'SALARY REPORT : GENERATED ON %s\n',SYSDATE); UTL_FILE.NEW_LINE(v_filehandle); for v_emp_rec in emp_info LOOP UTL_FILE.PUTF(v_filehandle,'DEPARTMENT: %s \n', v_emp_rec.deptno); UTL_FILE.PUTF(v_filehandle,' Employee:%s earns: %s\n', v_emp_rec.ename,v_emp_rec.sal); END LOOP; UTL_FILE.PUTF(v_filehandle, '*** END OF REPORT ***'); UTL_FILE.FCLOSE(v_filehandle); EXCEPTION WHEN UTL_FILE.INVALID_FILEHANDLE THEN raise_application_error(-20001,'Invalid File'); WHEN UTL_FILE.WRITE_ERROR THEN raise_application_error(-20002,'Unable to write file'); End;

Steps for executing the procedure are as follows


137

1. In the init.ora file of the database, the file location has to be defined as a value of UTL_FILE_DIR Open the init.ora file and add the property as followsUTL_FILE_DIR = C:\ABC (Step 1 is only required for early version. If directory object is not created then UTL_FILE_DIR path is required) 2. Create a folder ABC in C drive. 3. Create a directory object in database as create directory D1 as 'C:\ABC' Directory is a nonschema database object that provides a way for administering the o.s. files through Oracle Server 9i. A Directory specifies an alias for the actual o.s. path . 4. Execute the procedure Exec Sal_Status('D1','a1.txt'); -- a1.txt is the new text file getting generated. Query the following dictionary views to get information on directories in the database ALL_DIRECTORIES - all directories accessible to the database user

138

5) DBMS_SESSION Package
1) The global variables value can be re initialized. create or replace package p as g number := 0; end; / create or replace procedure pr1 is begin p.g := p.g + 1; dbms_output.put_line('Value of g is ' || p.g); end; / exec pr1; exec pr1; exec pr1;

Index

139

After executing the modify_package_state procedure the initial value of the global variable g gets initialized.

2) Cursors can get reinitialized The cursors which are already open in that session can naturally get closed after closing the session only. But if the cursor needs to be explicitly closed then the dbms_session.modify_package_state procedure will do that since the session is getting reinitialized. create or replace package pack1 140

as cursor cf is select ename,sal from emp; procedure p1; end pack1; / create or replace package body pack1 as procedure p1 is M cf%rowtype; begin open cf; fetch cf into M; dbms_output.put_line(M.ename || ' - ' || M.sal); end; end; / exec pack1.p1 -- Shows the first record exec pack1.p1 -- Throws the error begin dbms_session.modify_package_state(dbms_session.reinitialize); end; / Set ServerOutPut On exec pack1.p1 3) Re-initializing the One-time procedure values Suppose the one time procedure loads the names of the employees into a plsql table using a cursor, then the names list would be very much session specific. If the names are updated independently and committed still the same old name list would be continued. To refresh the plsql table the dbms_session.modify_package_state has to be used. Example create or replace package pack1 as type emp_list is table of Emp.Ename%type index by Binary_Integer;

141

cursor cf is select ename from emp; procedure show_name; V emp_list; end; / create or replace package body pack1 as i number := 1; procedure show_name is begin For a in V.First .. V.last Loop dbms_output.put_line(V(a)); End Loop; end; Begin Open cf; loop Exit when cf%notfound; Fetch cf into V(i); i := i + 1; end loop; end; / exec pack1.show_name; update emp set ename = 'SMITH123' where ename = 'SMITH'; Commit; exec pack1.show_name; -- Even if committed still shows SIMTH begin dbms_session.modify_package_state(dbms_session.reinitialize); end; / Set ServeroutPut On exec pack1.show_name; -- Now shows the changed name SMITH123 !

142

Topic 13. Managing Dependencies


CASE FIRST CREATE OR REPLACE VIEW V1 AS SELECT * FROM TX1; ALTER TABLE TX1 ADD C NUMBER; DESC V1; SELECT * FROM V1; DESC V1; CASE TWO CREATE OR REPLACE PUBLIC SYNONYM S22 FOR TX1; CREATE OR REPLACE PROCEDURE PP5 IS X NUMBER; BEGIN SELECT A INTO X FROM S22; END; CREATE TABLE S22(Y VARCHAR2(10)); EXEC PP5; DROP TABLE S22; EXEC PP5;

Index

143

CASE THREE
create or replace procedure query_emp is x number; begin select sal into x from emp where ename = 'JAMES'; dbms_output.put_line(x); end; create or replace view emp_vw as select * from emp; create or replace procedure add_emp is begin update emp_vw set sal = sal+ 10000 where ename = 'JAMES'; end; Changes --1) The internal logic of the Query_EMP procedure is modified. create or replace procedure query_emp is x number; begin select sal into x from emp where ename = 'ALLEN'; dbms_output.put_line(x); end; EXEC ADD_EMP; 2) A new column is added to the EMP table. alter table emp add xy number; EXEC ADD_EMP; 3) The EMP_VW is dropped. 144

drop view emp_vw; EXEC ADD_EMP;

CASE FOUR

Drop package PACKMD; CREATE OR REPLACE PACKAGE PACKMD AS PROCEDURE P1; END;
CREATE OR REPLACE PACKAGE BODY PACKMD AS PROCEDURE P1 is begin dbms_output.put_line('AAAA'); end; END;

create or replace procedure standalone is begin PACKMD.P1; END; EXEC STANDALONE Change the body of the package CREATE OR REPLACE PACKAGE BODY PACKMD AS PROCEDURE P1 is begin dbms_output.put_line('BBBB'); end; 145

END; select status from user_objects where object_name='STANDALONE'; Now change the specification of the package CREATE OR REPLACE PACKAGE PACKMD AS PROCEDURE P1; procedure p2; END; select status from user_objects where object_name='STANDALONE';

Displaying direct and Indirect dependencies


Step 1 Run the script utldtree.sql that creates the objects that enable you to display the direct and indirect dependencies. (This script is in ORACLE_HOME/rdbms/admin folder) Scenario Table is emp EMP table has two direct dependent objects of type view and trigger. create or replace view V_Emp as select * from emp1 where job = 'MANAGER' / create or replace trigger E_Trig after insert on emp1 for each row begin dbms_output.put_line('Record inserted in emp table'); end; / There is a procedure P_V_Emp which is based on view V_Emp. So there is indirect dependency of table emp with P_V_Emp procedure.

146

create or replace procedure P_V_Emp(no emp1.empno%type, name emp1.ename%type, j emp1.job%type, s emp1.sal%type DEFAULT 'MANAGER') is begin delete from V_EMP; Insert into V_Emp(empno,ename,job,sal) values(no,name,j,s); end; / Now there is a procedure P2 which calls P_V_Emp create or replace procedure p2 is begin P_V_Emp(123,'abc', 8000); end; / To get the list of dependencies execute the procedure deptree_fill EXECUTE deptree_fill('TABLE','SCOTT','EMP1'); DEPTREE VIEW select nested_level, type, name from deptree /

To display an indented representation of all the dependent objects query ideptree view.

147

148

Topic 14. REF CURSORS


Ref Cursor is actually a kind of data type in plsql. Basically it is used to make the concept of cursor more dynamic.

Index

We can use the same type of cursor for different query in the same program.

Three step process Declare a Type of Ref Cursor Declare a variable of that type Open the cursor by giving the query in the Begin Section instead of query being given in the Declare Section.
Introduction to REF CURSOR A REF CURSOR is basically a data type. A variable created based on such a data type is generally called a cursor variable. A cursor variable can be associated with different queries at run-time. The primary advantage of using cursor variables is their capability to pass result sets between sub programs (like stored procedures, functions, packages etc.). Let us start with a small sub-program as follows:

declare type r_cursor is REF CURSOR; c_emp r_cursor; en emp.ename%type; begin open c_emp for select ename from emp; loop fetch c_emp into en; exit when c_emp%notfound; dbms_output.put_line(en); end loop; close c_emp; end;
Let me explain step by step. The following is the first statement you need to understand: type r_cursor is REF CURSOR; The above statement simply defines a new data type called "r_cursor," which is of the type REF CURSOR. We declare a cursor variable named "c_emp" based on the type "r_cursor" as follows: c_emp r_cursor;

149

Every cursor variable must be opened with an associated SELECT statement as follows: open c_emp for select ename from emp; To retrieve each row of information from the cursor, I used a loop together with a FETCH statement as follows: loop fetch c_emp into en; exit when c_emp%notfound; dbms_output.put_line(en); end loop; I finally closed the cursor using the following statement: close c_emp; %ROWTYPE with REF CURSOR In the previous section, I retrieved only one column (ename) of information using REF CURSOR. Now I would like to retrieve more than one column (or entire row) of information using the same. Let us consider the following example:

declare type r_cursor is REF CURSOR; c_emp r_cursor; er emp%rowtype; begin open c_emp for select * from emp; loop fetch c_emp into er; exit when c_emp%notfound; dbms_output.put_line(er.ename || ' - ' || er.sal); end loop; close c_emp; end;
In the above example, the only crucial declaration is the following: er emp%rowtype; The above declares a variable named "er," which can hold an entire row from the "emp" table. To retrieve the values (of each column) from that variable, we use the dot notation as follows: dbms_output.put_line(er.ename || ' - ' || er.sal); Let us consider that a table contains forty columns and I would like to retrieve fifteen columns. In such scenarios, it is a bad idea to retrieve all forty columns of information. At the same time, declaring and working with fifteen variables would be bit clumsy. The next section will explain how to solve such issues. Working with REF CURSOR in PL/SQL - Working with RECORD and REF CURSOR

150

Until now, we have been working either with %TYPE or %ROWTYPE. This means we are working with either one value or one complete record. How do we create our own data type, with our own specified number of values to hold? This is where TYPE and RECORD come in. Let us consider the following example:

declare type r_cursor is REF CURSOR; c_emp r_cursor; type rec_emp is record ( name varchar2(20), sal number(6) ); er rec_emp; begin open c_emp for select ename,sal from emp; loop fetch c_emp into er; exit when c_emp%notfound; dbms_output.put_line(er.name || ' - ' || er.sal); end loop; close c_emp; end;
The most confusing aspect from the above program is the following: type rec_emp is record ( name varchar2(20), sal number(6) ); The above defines a new data type named "rec_emp" (just like %ROWTYPE with limited specified fields) which can hold two fields, namely "name" and "sal." er rec_emp; The above statement declares a variable "er" based on the datatype "rec_emp." This means that "er" internally contains the fields "name" and "job." fetch c_emp into er; The above statement pulls out a row of information (in this case "ename" and "sal") and places the same into the fields "name" and "sal" of the variable "er." Finally, I display both of those values using the following statement: dbms_output.put_line(er.name || ' - ' || er.sal);

151

Working with REF CURSOR in PL/SQL - Working with more than one query with the same REF CURSOR

As defined earlier, a REF CURSOR can be associated with more than one SELECT statement at runtime. Before associating a new SELECT statement, we need to close the CURSOR. Let us have an example as follows:

declare type r_cursor is REF CURSOR; c_emp r_cursor; type rec_emp is record ( name varchar2(20), sal number(6) ); er rec_emp; begin open c_emp for select ename,sal from emp where deptno = 10; dbms_output.put_line('Department: 10'); dbms_output.put_line('--------------'); loop fetch c_emp into er; exit when c_emp%notfound; dbms_output.put_line(er.name || ' - ' || er.sal); end loop; close c_emp; open c_emp for select ename,sal from emp where deptno = 20; dbms_output.put_line('Department: 20'); dbms_output.put_line('--------------'); loop fetch c_emp into er; exit when c_emp%notfound; dbms_output.put_line(er.name || ' - ' || er.sal); end loop; close c_emp; end;
In the above program, the skeleton looks like the following: declare . . Begin . . open c_emp for select ename,sal from emp where deptno = 10; . . fetch c_emp into er; . .

152

close c_emp; . . . . . . close c_emp; . . end; From the above skeleton, you can easily understand that every CURSOR is opened, used and closed before opening the same with the next SELECT statement. open c_emp for select ename,sal from emp where deptno = 20; fetch c_emp into er;

153

ORDBMS Topic 15. TYPES Index


Users can create their own data types by combining previously defined data types Also called Object types Object types made up of attributes and members Creating type Info which holds the attributes Create or replace type Info as OBJECT ( Name varchar2(30), Address varchar2(30), City varchar2(20), Phone number(7)); To see the structure of the object Desc Info Creating Relational Table with User-Defined Type Using this type in table E. E table becomes a relational table due to the use of Info user defined datatype. Create table E (Empno number, Emp_Det Info, Sal number); See the structure of this relational table E Desc E To see the attributes in the Desc command for a relational table To see the attributes and the datatypes of the user defined datatype set describe depth 2 Desc E Inserting data in the table E Insert into E Values (1, Info(abc,kothrud,Pune,7876), 7000);

154

Validations to the Column of user defined datatype Only Not Null can be given. create table f57 (a number, b info not null) Note Primary key, Unique, Foreign Key and check constraint cannot be given to the column of user defined data type. Constraint can be given to the attributes of the column for that specific table. For the name attribute of det column giving constraint Syntax -- ColumnName.AttributeName create table j1 (roll number, det info, constraint ccc unique(det.name)); Accessing the values of the attribute columns To see the name and city for Empno 1 -- To view an attributes value from user-defined types, dot notation is used Alias name is a must for the table Values of the tables can be referred using dot notation prefixed with table alias Select X.Emp_Det.Name, X.Emp_Det.City, Empno from E X where Empno = 1; To see the records when the condition is having an attribute column Select * from E X where X.Emp_Det.Name = abc;

155

Altering types
Dropping the type To drop a type first all the tables in which that type is used should be dropped , then only the type can be dropped. User_Types is the in built table for type information. select type_name,typecode from user_types; Altering types The attributes data type size can be increased at any time. create or replace type h as object (x number(3), y number(4)); /*Can increase the size.*/ alter type h modify attribute x number(10); /*Cannot decrease the size. */ alter type h modify attribute x number(9); Once a attribute is defined in the type then in future its data type cannot be changed. Only its size can be increased. Can drop an attribute provided the type is not used in a table. alter type h drop attribute x; Adding an attribute after type creation. create or replace type t1 as object (a number); alter type t1 add attribute b number;

156

Introduction to Methods A method is a subprogram declared in an object type specification using the keyword MEMBER A method is procedure or function that is part of the object type definition A method cannot have the same name as the object type or any of its attributes Like packaged programs, most methods have two parts Specification consists of method name an optional parameter list for functions, a return type Body is the code that executes to perform a specific operation The methods of an object define the behavior of the object These methods are called MEMBER methods Member methods are first defined in the object type and then the method body is created Methods are defined in the Object type with the CREATE statement Syntax for Method Specification-CREATE [OR REPLACE] TYPE <typename> AS OBJECT (attribute1 datatype, : attributeN datatype MEMBER PROCEDURE <methodname> (parameter, mode, datatype), MEMBER FUNCTION <methodname> (parameter, mode, datatype) RETURN datatype; Method body is also created with the CREATE statement CREATE [OR REPLACE] TYPE BODY <typename> AS MEMBER FUNCTION <methodname> (parameter dataype) RETURN <datatype> IS <PL/SQL_block>; MEMBER PROCEDURE <methodname>(parameter datatype); END; Procedure inside a type: create type address as object ( street varchar2(10), city varchar2(10), state varchar2(10), member procedure changeadd(st_n varchar2, st_c varchar2, st_s varchar2) );

157

/ create or replace type body address is member procedure changeadd(st_n varchar2, st_c varchar2, st_s varchar2) is begin if (st_n is null) or (st_c is null) or st_s is null or (upper(st_s) not in ('UK','US','CA') ) then raise_application_error(-20001,'INVAID DATA'); else street := st_s; city := st_c; state := st_s; end if; end changeadd; end; / Implementation: declare add1 address; begin add1 := address('a','b','c'); -- Initializing add1.changeadd('x','y','z'); dbms_output.put_line(add1.state); end; / declare add1 address; begin add1 := address('a','b','c'); -- Initializing add1.changeadd('x','y','us'); dbms_output.put_line(add1.state); end; / Function inside a type: Example 1: create or replace type area as object (length_c integer, breadth_c integer, height_c integer, member function cube_cal return integer

158

); create or replace type body area as member function cube_cal return integer is v_area integer; begin v_area := length_c * breadth_c * height_c; return v_area; end cube_cal; end; Implementation: declare c area; a integer; begin c := area(3,4,5); a := c.cube_cal; dbms_output.put_line('The area is ' || a); end; / Example 2: Create a function with its code inside a type. Create a object table of that type. Then the function can be called directly in the select statement by passing column name as a parameter. (E.g. -- Select sum(sal) from emp) Advantage -- No need of writing the PL/SQL block for calling the function. Example of Method --Step 1 Creating Method specification -create or replace type results as object (name varchar2(50), marks number, member function resultsfun(marks In Number) return char );

159

Step2 Creating Method Body ---create or replace type body results as member function resultsfun(marks In Number) return char is disp char(20); begin if marks >= 35 then disp := 'Passed'; else disp := 'Failed'; end if; return disp; end; end; Step 3 --- Creating Object table student based on the above type create table students of results; Step 4 Inserting two records --insert into students values('John',90); insert into students values('Smith',23); Step 5 --- Calling Object Methods Object methods can be called in a query, after an Object table has been created based on the type SELECT <columnname>, <aliasname>.<methodname(parameters)> FROM <tablename> <aliasname>; Select name, f.resultsfun(marks) RESULT from students f ;

160

Ordering Data using the MAP method Map method will be used when the data of a user defined column needs to be sorted. Instances of an object type have no predefined order To put them in order, use a MAP method A MAP method is a parameter less function with a scalar return type of DATE, NUMBER, VARCHAR2, or an ANSI SQL type such as CHARACTER or REAL A MAP method maps object values into scalar values (which are easier to compare), then compares the scalar values Example Step 1 -- Create the type specification create or replace type company as object (city varchar2(20), sales number, MAP MEMBER FUNCTION sorting return number); Step 2 Create the type body in which sorting is mentioned. --create or replace type body company as MAP MEMBER FUNCTION sorting return number is begin return SELF.sales; end; end; Step 3 Create the relational table create table tata_motors (year varchar2(30), details company); Step 4 Insert records insert into tata_motors values('1999',company('pune',900)); insert into tata_motors values('2000',company('mumbai',600)); Step 6 Due to the use of MAP method we can now sort the Details column due to which the sales figures will get sorted. select * from tata_motors order by details;

161

Topic 16. VARRAY


Index

Also known as varying arrays It is an aggregation of values stored in a single column A single column in the database would point to a group of elements Can associate a single identifier with an entire collection Can reference the entire collection as a whole or access the elements individually To access individual elements, use standard subscripting syntax i.e. array_name(subscript) Maximum size must be specified at time of definition Index has a fixed lower bound of 1 and an extensible upper bound A varying array allows to store repeating attributes of a record in a single row. Steps for Creating VARRAYs Create a type which will hold information for a single line item Create an array of type known as Varray type, holding multiple values of a particular type Create a table having one of the columns, known as varray column, based on the Varray type The SYNTAX for creating a VARRAY CREATE TYPE <varray_type_name> AS VARRAY(limit) OF <type>; Example In a factory table there are workers using different tools. Each worker is using say 3 numbers of tools. Thus if a single worker is taking 3 tools then that workers name will come thrice in the table. So there will be data redundancy. Varrays will repeat values that change for a particular column which will save the storage space. A set of rows is repeated in one column in varray. Step 1 Create an varray holding 3 elements of varchar2(20). create or replace type TOOLS_VA as varray(3) of varchar2(20); Step 2 - Create a table having one of the column as the varray column based on the varray type. create table factory (name varchar2(20) primary key, tools TOOLS_VA);

162

Step 3 Inserting record in the factory table Insert into factory values ('John', TOOLS_VA ('Hammer', 'Sledge', 'Drill')); Insert into factory values ('Smith', TOOLS_VA ('Screw Gauge', 'Hammer', 'AX')); Insert into factory values ('Martin', TOOLS_VA (null, 'Sledge', null));

To see names of tools for each worker. select F.Name, V.* from Factory F, TABLE(F.Tools) V; V is an alias table created for the tool names. TABLE function is used to simplify the process of selecting data from varying arrays. Conditional records To see records of persons having Hammer tool select F.Name, V.* from Factory F, TABLE(F.Tools) V where V.Column_Value = 'Hammer' Summary records To see count of tools for each person select F.Name, Count(Column_Value) from Factory F, TABLE(F.Tools) V Group By F.Name

Updating VARRAYs Individual elements cannot be updated Entire VARRAY has to be updated if one of the elements has to be changed To change the value of first index tool of Martin from null to Vernier C update factory set tools = tools_va('Vernier C','Sledge', NULL) where name = 'Martin';

163

Creating customized and advance report using PL/SQL blocks Displaying data from the varray column declare cursor cf is select * from factory; vcf cf%rowtype; begin for vcf in cf loop /*This loop is for the normal column name.*/ dbms_output.put_line('Contact Name '|| vcf.name); for i in 1..vcf.tools.count loop /*This loop is for the number of tools for the current row's name*/ dbms_output.put_line('-------------' || vcf.tools(i)); end loop; dbms_output.put_line('----------------------------------'); end loop; end; / -----------------------------------------------------------------------------------------------------Displaying status for each record whether Hammer is given or not declare cursor cf is select * from factory; vcf cf%rowtype; x number; begin for vcf in cf loop x := 0; dbms_output.put_line('Contact Name -- '|| vcf.name); for i in 1..vcf.tools.count loop if vcf.tools(i) = 'Hammer' then x := x + 1; end if; end loop; if x > 0 then dbms_output.put_line('Hammer is supplied'); else dbms_output.put_line('Hammer is not supplied'); end if;

164

dbms_output.put_line('--------------------------- '); end loop; end; / To display the second tool provided for each person declare cursor cf is select * from factory; vcf cf%rowtype; x number; begin for vcf in cf loop x := 0; dbms_output.put_line('Contact Name -- '|| vcf.name); for i in 1..vcf.tools.count loop if i = 2 then dbms_output.put_line(vcf.tools(i)); end if; end loop; x := x + 1; end loop; end; /

Displaying the names of workers who have been given shafts. declare cursor cf is select * from factory; vcf cf%rowtype; countOfShaftHolders number; begin dbms_output.put_line('The Shaft holders are --> '); countOfShaftHolders := 0; for vcf in cf loop for i in 1..vcf.tools.count loop if vcf.tools(i)='Shaft' then dbms_output.put_line(vcf.name); countOfShaftHolders := countOfShaftHolders + 1; end if;

165

end loop; end loop; if countOfShaftHolders = 0 then dbms_output.put_line('Sorry, there are no shaft holders'); end if; end;

166

Creating a varray from a type Multiple attributes for a single record and multiple sets of attributes for a single record. create or replace type Monthly_Sales as Object (Month Varchar2(3), Sales Number(6)); / create or replace type Monthly_Sales_VA as varray(4) of Monthly_Sales; / create table city_details (name varchar2(15), performance Monthly_Sales_VA) / Insert into city_details values('Pune', Monthly_Sales_VA(Monthly_Sales('Jan',9000), Monthly_Sales('Feb',7500), Monthly_Sales('Mar',8000), Monthly_Sales('Apr',10000)) ) / select A.name, V.* from city_details A, TABLE(A.Performance) V; To see details of varray query the view user_coll_types select type_name,COLL_TYPE, UPPER_BOUND from user_coll_types

167

Topic 17. Nested Table


Index

Table within a table A table is represented as a column within another table There is no limit to the number of rows in the nested table for each row in the main table. Basically used for mapping master-detail relationships between tables. i.e. In the parent table there would be one column, which will store the location of the nested table. Steps for Creating Nested Tables Create a type which will hold information for a single line item (this represents the nested table details) Create Type of Table to hold details for the multiple rows of the nested table (this will be the nested details - nested table type) Create the master table with one of the columns which will hold the nested table type Creating a Nested Table SYNTAX CREATE TYPE <nested_table_type_name> AS TABLE OF <typename>; Example -There is main table Fare_Tab1. It has columns such as Route_Coe,Route_Desc,Origin,Destination, Firts_Fare, Bus_Fare,Eco_Fare and journey_hrs. There is a type as table Fs_Nst_Type. This type table holds column such as Flightno, Airbusno,Deprt_Time, Flight_Day1 and Flight_Day2. Now we are trying to establish the relation between Fare_Tab1 table and this type table Fs_Nst_Type. One route_code from Fare_Tab1 table can hold multiple Flightno, Airbusno,Deprt_Time, Flight_Day1 and Flight_Day2. For this purpose we will create a column in Fare_Tab1 table which will show details of the Fs_Nst_Type for a particular route_code. So that column actually represents the nested table.

168

Step 1 -- Create a type Flight_Sch_type to hold details for a single flight. Create or replace type Flight_Sch_Type as OBJECT (Flightno char(4), Airbusno char(4), Deprt_time char(6), Flight_day1 number(1), Flight_day2 number(1)); Step 2 -- Create a type of table which will hold the multiple rows of the nested table. Create type Fs_Nst_Type as TABLE of Flight_Sch_Type; Step3 -- Create the main table which will have the nested table through a column of the main table Create table Fare_Tab ( Route_Code char(7), Route_Desc varchar2(30), Origin varchar2(15), Destination Varchar2(15), First_fare number(5), Bus_Fare number(5), Eco_Fare number(5), Journey_Hrs char(5), Flight_Sch_Det Fs_Nst_Type ) Nested Table Flight_Sch_Det store as Fs_Store_Tab ; Fs_Store_Tab is system generated table for oracle's reference. Inserting records in the nested table through the main table SYNTAX INSERT INTO <master_table_name> VALUES(<master_table_column1_value>, <master_table_columnN_value, nested_table_type(nested_column(attribute1_value>,...<attributeN_value>), nested_column(attribute1_value>, <attributeN_value>)));

169

Insert into Fare_Tab Values ('Goa-Ban', 'Goa - Bangalore', 'Goa', 'Bangalore', 450, 300, 200, 3, Fs_Nst_Type ( Flight_Sch_Type('F2', 'AB01',' 9:00',1,3), Flight_Sch_Type('F3','AB02', '11:00', 3, 5) ) ); Here in this insert statement for one route_code 'Goa-Ban' 2 nested rows of F2 and F3 are inserted. Displaying the data of the nested table columns - SYNTAX in Oracle 8i SELECT <master_table_columns>, <nested_alias>.<attributes_from_type> FROM THE (SELECT <nested_column> FROM <master_table> WHERE <master_table_condition>) <nested_alias>, <master_table> WHERE <nested_table_condition>; To display the nested row for the route code 'Goa-Ban' for flighno F1 Select NT.flight_day1, NT.flight_day2 ,Nt.Deprt_Time, route_code from THE (Select Flight_Sch_Det from Fare_Tab1 where route_code = 'Goa-Ban') NT, Fare_Tab1 where NT.Flightno = 'F2'; In Oracle 9i Table function is used instead of THE clause which makes the query very easy to understand select route_code, NT.flight_day1, Nt.flight_day2,NT.Deprt_Time,NT.flightno from Fare_Tab, Table(Fare_Tab.Flight_Sch_Det) NT where route_code = 'Goa-Ban' And NT.flightno = 'F3' So Syntax in 9i is Select master_table_cols, Alias.Neste_Table_Cols From Master_Table, Table(Master_Table.Nested_Table) Alias To see all attribute columns of the Nested Table we can use * with alias name of the nested table select route_code, NT.*

170

from Fare_Tab1, Table(Fare_Tab1.Flight_Sch_Det) NT where route_code = 'Goa-Ban' And NT.flightno = 'F3' Updating the nested row in 8i SYNTAX UPDATE THE (SELECT <nested_column> FROM <master_table> WHERE <master_table_condition>) <nested_alias> SET <nested_alias>.<attribute1_name> = <value>,..., WHERE <nested_table_condition>; To change the value of flight_day1 from 3 to 5 for the Flightno F3 on the route_code 'Goa-Ban' Update THE (Select Flight_Sch_Det from Fare_Tab1 F where F.route_code = 'Goa-Ban') NT Set NT.Flight_day1 = 5 where NT.Flightno = 'F3'; F is the alias name for the master table Fare_Tab NT is the alias name for the nested table. In 9i the same Update statement will be as follows To change the Flightno of the nested table to F11 update TABLE(select FLIGHT_SCH_DET from Fare_Tab where route_code = 'Goa-Ban') NT Set NT.Flightno = 'F11' where NT.Flight_Day2 = 5; Syntax in 9i Update TABLE(select nested_table_name) From Master_Table Where master_table condition) Alias for Nested_Table Set Alis.Nested_Col = <new value> Where Alias.Nested_Col Condition;

171

Deleting a row from the nested table SYNTAX in 9i DELETE FROM THE (SELECT <nested_column> FROM <master_table> WHERE <master_table_condition>) <nested_alias> WHERE <nested_table_condition>; To delete the details of flightno 'F2' for the route_code 'Goa-Ban' Delete From THE (Select Flight_Sch_Det from Fare_Tab1 F where F.Route_Code = 'Goa-Ban') NT where NT.Flightno = 'F2'; Deleting a row in 9i To delete a nested row of Flight_day2 having value 5 for a route_code Goa-Ban Delete Table(select FLIGHT_SCH_DET from Fare_Tab where route_code = 'Goa-Ban') NT where NT.Flight_Day2 = 5; Inserting a nested row to route_code Goa-Ban (9i) Insert into TABLE (Select FLIGHT_SCH_DET from Fare_Tab where route_code = 'Goa-Ban') Values (Flight_Sch_Type ('F22','AB02','3',3,5)) To see number of records in the nested table select count(*) from Fare_Tab, Table(Fare_Tab.Flight_Sch_Det) NT;

172

When to use Nested Tables instead of VARRAYs When the order of the data elements is not important When indexing is required on an attribute of the nested table type When there is no set limit to the number of entries for the data elements When the data elements need to be queried

173

ASSIGNMENTS
1. Programming Basics Assignments

Index

1) Write a plsql block to accept the empno from the user. Raise the salary of that employee by 20% and display the raised salary. (Note - Do not update the salary, just display the changed salary) 2) Accept job type from the user. Display the message depending upon whether no rows or one row or several rows are selected. The message should be any one from the below 3 as per the situation. JOB TYPE FOUND ONCE JOB TYPE FOUND MORE THAN ONCE JOB TYPE NOT FOUND 3) Using basic loop technique display all the multiples of 7 between 31 and 48m. 4) Write a block to accept the Empno from the user and change the salary according to the following condition. If salary is in the range of RANGE INCREMNENT 1000 2000 500 2001 3000 1000 3001 4000 1500 >4000 2000 (Note - Do not update the salary, just display the changed salary) 5) Create a table Inspection_Details_EmployeeID that has one column Readings of numeric type. Using pl/sql block add numbers, which has the difference of 0.1. The numbers should be between 0.1 and 6.8. 6) Through while loop display the multiples of 7 till 70 in the descending

order. 7) Display the difference of salary between the oldest and the latest employee. 8) Create the table tx1 with the following script.
Create table Oracle_Batch(student_name varchar (20);

Create a program that will accept the student_name form the user and if the user has entered all the characters as alphabets only then enter that name into the Oracle_Batch table.

174

9) Write a PL/SQL code to accept an employee number from the

user and display whether it is exists or not .


10) Write a PL/SQL code to display the name, salary and grade of

the employee by accepting employee code. Grade is A if salary >25000, B if salary > 15000, C for other salaries. Use CASE statement only.

175

2. Cursor Assignments
Using the cursor mechanism write the following PLSQL blocks.

Index

1) Check whether the 3rd Clerks salary is greater than the 2nd Clerks salary. If it is so, then display message as Third has more salary than the second otherwise display message as Second has more salary than the first 2) Display the sum of first five salaries from the emp table. 3) The management has decided to increase salary of employees by 10%. Starting with lowest paid earner and working up. If at any time salary bill exceeds 45000/- then no further employees are to be given an increase. 4) Display the names of the employees who are earning salary less than the average salary of their own jobs. 5) Use a parameterized cursor which will take deptno as the parameter and will display the highest two salaries of that deptno. 7) Create the table Emp_Coupons with the two fields Name and Coupon_No Enter the following records in it. John Martin Allen Roger Adams Kim 80 83 87 78 88 89

Make a PLSQL block that will check whether the coupon number of the current record is greater than the previous. If any records coupon number is less than the previous one then display the name of the person whose coupon number is less. 8) Display the name, sal and the cumulative total of salaries. Cumulative salary would be first salary +0 for the first record, second + first for the second and so on. 9) Comparing the total of salaries of the even number records and the odd number records from the emp table and display the highest total. 10) Check the total of salaries of the first 3 employees. If that total is greater than or equal to 3500 then increment those first 3 employees salaries by 15% else do not increment the salary.

176

3. PLSQL Table Assignments

Index

1) Add multiples of 5 from 5 to 130 in a plsql table and display them from the plsql table in the descending order. 2) Using a cursor add the rows of all SALESMAN in the plsql table and then display only the last row of that plsql table 3) Take all the salaries from the emp table in a plsql block using a cursor. Then display only those salaries from the plsql block which have value >= 2800.

177

4. Exception Handling Assignments


1. Accept the name of the employee. Display the salary of that employee. Handle all possible run-time errors. 2. Create the ex_tab table with the following specifications: Name A B C D E Data type Integer Integer Char(3) Integer Integer Constraint Primary key Unique Not Null Must be >= 10 Foreign key from A column

Index

For trapping all the constraint violation errors create another table error_log that has 3 columns Record_Number, Error_Number and Error_Message. Through a plsql block accept all the 5 column values from the user run-time, if any error violation takes place then the error_log table should get the record displaying the details of that error. If there is no error then the record should get added in the ex_tab table. 3. Create a table Emp_Details which has the following specifications.
Name Empno Membership_no Name Salary Mgr Data type Integer Char Varchar Integer Integer Constraint Primary Key Unique Not Null Above 10000 Refers to the Empno column

Take all the column values from the user and enter the record in the table Emp_Details. If any constraint violation happens then provide user friendly error message according to the error.

178

4.

Run the following script.


create table mm1 (empid number, ename varchar2(10), job varchar2(10)); insert into mm1 values(1,'John','Manager'); insert into mm1 values(2,'Martin','Clerk'); insert into mm1 values(3,'Smith','Clerk');

Create a plsql block which will accept empid, ename and job from the user. If the complete record is duplicated then raise an exception FOUND_DUPLICATE_RECORD. If the record in not duplicate then add it into the mm1 table.
5. Create a table Customer_Data which has 5 fields like Custid, Qty, Required_In_Days, Qty_Per_Day and Rate_Per_Day. Take the first 3 columns values from the user. You want to calculate Quantity per day which would be qty given divided by Required_In_Days. Once Quantity per day is calculated do the calculation of Rate_Per_Day which will be Quantity per day * 100. Finally when all the values are ready add a new record in the table Customer_Data. Handle all types of error that may occur at different point of times.

179

5. Procedures Assignments

Index

1. Create a procedure named Job_Details, which will show the total, highest, lowest and average of the salary for the job. (Job type is the parameter to be passed.) 2. Create a procedure named Change_Salary that will accept salary and will return the 40% raised salary. The value returned should be given to a separate parameter. 3. Create a table Emp_Proc having structure and records same as that of emp table. Create a procedure Delete_Employee that will take department name as a parameter. The procedure should delete the record of the employees from the Emp_EmployeeID table only for the department name passed. 4. Create a procedure, NEW_EMP to insert a new employee into the Emp_Proc table . The procedure should ensure whether the department ID specified for the new employee exists in the DEPARTMENTS table. 5. Create a table Company_Data with columns empid, ename and job. There is no primary key or unique key constraint to the empid column. Create a procedure Add_Company_Data which will take empid as the parameter and adds a new record in that table if the empid with the same value does not exist. 6. Create a table Emp_Sal that will have 2 columns Empno and Gross_Salary Create a procedure Salary_Details which will take Empno,Basic salary and Allowance as the three parameters from the user. There has to be a sub procedure Tax_Calculations that will calculate the tax value as per the salary values given. There is a small chart for tax calculations. Basic Above or equal to 5000 Between 4000 and 4999 Between 3000 and 3999 Between 2000 and 2999 Less than 2000 Tax 20% of the basic 15% of the basic 10% of basic and 2% of Allowance 5% of basic and 1% of Allowance 100

Once the tax value is calculated the Gross calculation will be done as ( Basic +Allowance ) Tax. After calculating the tax main procedure will add the record in the Emp_sal table. 7. Considering the emp table create a procedure IsHighest that will take the ename as the parameter. The procedure should reply whether that employee is the highest earner or not. 180

6. FUNCTIONS Assignments

Index

4. Create a table student. It has four fields rollno, name, marks and grade. Note Grade column has be generated by a function get_Grade(). This function takes marks as argument and generates the grade as per the following table Marks Criteria Greater than or equal to 70 Between 60 and 69 Between 50 and 59 Less than 50 Grade Distinction First Class Second Class Failed.

When the record is inserted then the grade value should be generated. 5. Create a function Raise_Sal that will accept salary as a parameter and returns 15% raised salary. Use this function in the select statement of the emp table to see the result. 6. Create a function Yearly_Raise that will take the salary, deptno and job as the parameters and raise the salary according to different criteria. Criteria Clerk employees of deptno 20 earning salary above 1000 Clerk employees of deptno 20 earning salary less 1000 Clerk employees of deptno 20 earning salary above 1000 Clerk employees of deptno 20 earning salary less than 1000 Clerk employees of deptno 30 having any salary Raise 20% 15% 25% 18% 10%

181

Use this function to update salaries of the employees of job Clerk in the table emp. 4. Run the following script. Create Table Company_Product (ProductId Varchar(20)); Create a function check_productid that will take a productid (string) as a parameter. The function should check the last 4 characters of that productid. If they are KPIT (only upper case) then a new record must get created in the table Company_Product else throw a relevant error message.

182

5. Considering the emp table create a function Last_Employee which will take the job type as the parameter and display the last employee (s) joined in that job. 6. Considering the emp table create a function TopN_Salary that will take the top Nth number as the parameter and returns the highest salary at that position.

183

7. Packages Assignments

Index

1. Create a package Clerk_Salary. This package should have the following members. a. Basic has a fixed value of 10000. Only package members should be able to use the basic figure. b. Function get_HRA(). It should be 40% of basic. c. Function get_DA(). It should be 35% of basic. d. Function get_TA(). It should be 10% of HRA. e. Function get_PF(). It should be 15% of (basic+hra+da+ta) f. Function get_TAX(). It should be a fixed value 7500. g. Function get_Gross(). It should be basic+hra+da+ta (pf + tax). h. Procedure display_Gross. It should show the gross figure. Execute the display_Gross procedure and display the gross amount. 2. Create a package OverLoaded_Total that has function Show_total. This function takes salary and/or commission as parameters and returns the total as: i. If only salary was passed then the total should be 30% raised salary. ii. If salary and commission were passed then the total should be salary+commission. 3. Create a package Emp_Package that will have a plsql table called Emp_Info containing Enames and Sal from the emp table. The package has two procedure Show_Clerks and Show_Managers which will display the enames and salaries of the Clerks and Managers respectively. These procedures should take the names and salaries from the Emp_Info plsql table only. The Emp_Info always should get loaded within the session first when any of the package member is getting called for the first time within that session.

184

8. Triggers Assignments
The following triggers have to be created on the emp and dept tables

Index

4. Create a trigger No_DML_in_Lunch, which will not allow records to be entered between 1 and 2 pm on weekdays. 5. Create a trigger DML_on_View which will allow insert, update and delete for a view which is based on an inner join between tables Emp and Dept 6. Create a trigger ClerkAndAnalystOnly, which will allow records getting inserted in the table emp if the job of the employee is CLERK and ANALYST only. 4. Create a table Emp_Summary that has 4 columns Record number, Lowest_Sal Highest_Sal and Average_Sal. (Record_no is the auto generated number). Create a trigger Emp_Stats that will add a new record into the table Emp_Summary, which will get the auto record number, lowest, highest and the average salaries of the emp table, whenever a new record is added in the emp table or whenever any record is updated.

5. Create a table New_Top_employees that will have empno,ename,sal as the columns. Create a trigger Check_New_Top that will get fired whenever a new employee record is added in the emp table. If the new records salary is greater than or equal to 2500 then only that records empno, name and salary should be transferred into the New_Top_employees table.

185

9. Dynamic SQL Assignments

Index

1. Create a procedure Create_Table that will take the table name as the parameter and create a table with KPIT_ as the prefix for the table name which is given as the parameter. The table should have columns as KPNo, Name and BU 2. Create a procedure Drop_Table that will take the table name as the parameter. The procedure should only drop the table if the Number of rows within that table are less than 10. 3. Create a procedure Alter_table that will take table name, column name and data type as 3 parameters. The column name is the column whose data type has to be changed and the data type is the new data type to be given to that column. The procedure should only change the data type if that column is empty, else appropriate error message has to be flashed.

186

10.Varray Assignment Create a varray Shift_Data that has values of maximum 3 shifts. Create a table production that has the following columns: Name Production_id P_Date Shift Data type Integer Date Shift_Data (Varray)

Index

Enter two records in the production tables with each record having the 3 values for the shifts.

187

11. Nested Table Assignments Create a type match_details that has the following attributes: Name MId M_date Opponent_team Tickets_Sold Data type Integer Date Varchar2 Integer

Index

Create a nested table type match_details_nst based on the above type. Create a table Ground_Data that has the following columns: Name G_Id G_Name Area Match_Data Data type Integer Varchar2 Number match_details_nst (Nested Table)

Enter two records in the Ground_data table with 2 nested records for each main record.

---------------------------- xxxxxxxxx

--------------------------------

188