PLSQL Ora7

Download as doc, pdf, or txt
Download as doc, pdf, or txt
You are on page 1of 58

‫الـع ــالـــميـــة‬

‫للكمــبـيـوتـر‬

What is PL/SQL and why to use it?

PL/SQL is the procedural extension for SQL, which has many programming
languages properties; loops, if clauses … etc.

This extension has many advantages:

Enables the programmer to use local variables.


Define some user-defined data types.
Simplify complex programs by writing the program in blocks.
Write error-handling blocks, which are known as exceptions.

And many other benefits which will be introduced later on.

PL/SQL BLOCK

DECLARE -- optional

DECLARATION SECTION

BEGIN -- mandatory

BODY STATEMENTS
( SQL and PL/SQL Statements )

EXCEPTION -- Optional

STATEMENTS

END; -- mandatory

Where in the Declarative part, one can declare:


Variables, Cursors, PL/SQL tables, Constants, Exceptions … etc.

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
1
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

In the Body part the SQL and PL/SQL statements to be executed are written
to manipulate data.

In the Exception part statements are written to handle errors and exceptions,
which may also include SQL code.

PL/SQL code could be written in different ways:

1) Anonymous Blocks: which are usually saved in a file (with .sql


extension ) and are run when needed from this file.
2) Stored Procedure and Functions: which are named PL/SQL
code, saved in the data dictionary of the database.
3) Packages: which are named groups of procedures and
functions.
( Procedure, functions and procedures inside packages are
invoked by calling them by their names ).
4) Database Triggers: which are PL/SQL code that is related to a
certain table and are executed automatically.
5) There are also Application Procedure, Application Functions and
Application Triggers, which are usually in the Oracle Forms.

The PL/SQL environment could be SQL*Plus, Procedure Builder or


Application Triggers and Procedures.
In this course we’ll concentrate on the SQL*Plus environment.

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
2
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

DECLARE -- optional

DECLARATION SECTION

BEGIN -- mandatory

BODY STATEMENTS
( SQL and PL/SQL Statements )

EXCEPTION -- Optional

STATEMENTS

END; -- mandatory

Variables

In PL/SQL variables are used to store values and reference them where:

 Declare variable and assign initial values for them in the declarative
part.
 Inside the PL/SQL block body new values might be assigned
 Parameters could be passed to the variable in the block or received
from the block.
 Variables values could be used in insert, update, delete … etc.
 Variables might be assigned with an initial vale.
 Variables could be declared as a NOT NULL variables.
 Constants are also declared in the declarative part, their values
should not be changed in the PL/SQL body part

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
3
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

Data Types in PL/SQL

• Binary Integer (-2 **31-1,2**31+1) signed integer fastest


• CHAR(n) n up to 32767 note : 255 for RDBMS fields.
• varchar2(n) n up to 32767 note : 2000 for RDBMS fields.
• varchar(n) same as varchar2 note : not recommended.
• long up to 32767 note: 2 GB for RDBMS fields.
• raw up to 32767 note: 255 for RDBMS fields
• long raw up to 32767 note: 2 GB for RDBMS fields
• Boolean True, False or Null
• Date Jan 1 4712 BC up to DEC 31, 4612 with time
• Rowid stores Oracle rowids in readable format
• Number(n,d) precision up to n=38 and d -84,127

• float
• Decimal
• Numeric
• Real
• Integer

Examples on DECLARATIONS

• v_birthday Date;
• v_hiredt Date := sysdate + 7;
• v_count1 integer := 6; -- Note initialized variable

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
4
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

• v_count_tot binary_integer := 0;
• acc_id varchar2(10) not null --not valid
• v_acc_id varchar2(10) not null := ‘SHC’

Note:- if a variable is declared not null it must be


immediately initialized in declaration section

• Gravity constant Real :=9.8;


• Velocity Real := 2.0;
• Time Real := 1.0;
• F_vel velocity - gravity x Time ;
• v_answer boolean := true;

Notes : A constant must be initialized in its declaration


Which will be the constants final value

• my_var number := 22 is identical to


• my_var number default 22;

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
5
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

• %TYPE Declaration
A variable could be declared using the %type attribute. Where the data
type for the variable depends on a previously declared variable or a
column in a table.

The general syntax is:

var1 Tablename.Field%Type;

Advantages of using %TYPE declaration method:

• The datatype might not be known to the developer at design time.


• The datatype might change for the column.

Examples:

v_empno emp.empno%type;

v_dept_name scott.dept.deptno%type;

The above two examples illustrates declaring variables depending on a


column in a table, the following shows how to declare a variable and using a
previously defined one.

v_tot number(9,2);

v_comp_tot v_tot%type;

note: If the column in the database is defined as a not null column, this is not
inherited by the variable.

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
6
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

• %ROWTYPE Declaration
A group of variables could be declared using the %rowtype attribute.
Where the data type for these variables depends on a previously
declared cursor or a table.

Note: cursors will be introduced later in this course.

The general syntax is:

var1 Tablename%rowtype;

Advantages of using %ROWTYPE declaration method:

• The datatype might not be known to the developer at design time.


• A column might be added to a table after building the PL/SQL block.

Examples:

emp_rec emp%rowtype;

dept_rec scott.dept%rowtype;

The above two examples illustrates declaring variables depending on a


column in a table, the following shows how to declare a variable and using a
previously defined cursor.

Cursor c1 is select empno, ename, sal from emp;

Sal_rec c1%rowtype;

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
7
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

Examples :

1) Declare
emp_job char(10);
Begin
select job into emp_job
from emp
where empno = 7499;
If emp_job .. etc
......
end if;
end;

The same example could be written in a different way

Declare
emp_job emp.job%type;
begin
select job into emp_job
from emp
where empno = 7499;
If emp_job .. etc
......
end if;
end;

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
8
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

2) Declare
v_dno number(4);
v_dname char(14);
v_loc dept.loc%type;
begin
select * into v_dno, v_dname, v_loc
from emp
where deptno = 10;
if v_dname = ‘ACCOUNTING’ Then
.....
end if;
end;

The same example could be written in a different way

Declare
dept_rec dept%rowtype;
begin
select * into dept_rec
from emp
where deptno = 10;
if dept_rec.dname = ‘ACCOUNTING’ Then
.....
end if;
end;

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
9
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

3) Declare
cursor c_emp is select empno, ename, sal
from emp;
emp_r1 c_emp%rowtype;
begin
……
if emp_r1.sal < 1000 then ….
….
end if;
end;

Note: cursors will be introduced later in this course.

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
10
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

PL/SQL TABLES

PL/SQL TABLES are very similar to arrays know in common programming


language.

To use PL/SQL tables, you must first Declare a new type called TABLE.
Then you define a variable of that type

A PL/SQL table must contain a primary key of datatype BINARY_INTEGER


which indexes the PL/SQL table in addition to the columns for the PL/SQL
table.

Syntax:

TYPE type_name IS TABLE OF data_type INDEX BY BINARY_INTEGER;

Variable_name type_name;

EXAMPLES:-

1) Declare
TYPE my_table IS TABLE OF CHAR(10)
INDEX BY BINARY_INTEGER;

scottab my_table;

2) Declare
TYPE new_type IS TABLE OF dept.loc%Type
INDEX BY BINARY_INTEGER;

arr1 new_type;
arr2 new_type;

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
11
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

3) Declare
Cursor C_mgr is select empno, mgr
from emp;
TYPE mgr_array IS TABLE OF C_MGR%Type
INDEX BY BINARY_INTEGER;

emp_rec mgr_array;

Please note that you cannot initialize the table at declaration.


You can reference the PL/SQL table in the traditional way an array is
referenced in common programming languages, i.e.

scottab(1) := sal*12;

Or by a loop

For i in 1 ..20 loop


scottab(i) := i*10;
End Loop;

Note: loops will be introduced later in this course.

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
12
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

PLSQL RECORDS
A PL/SQL record is similar in structure to records in 3GL. And they are not the
same as a row in database table.

It allows the programmer to treat all the fields in the PL/SQL record as one
unit.

They are usually used to fetch a row of data from a table for processing it in a
PL/SQL block.

To use PL/SQL records, you must first declare a new type called RECORD.
Then you define a variable of that type.

You can define a record that holds the same structure of a record in the
database (but it is NOT the same as a row in the database) as follows:-

Syntax:

TYPE type_name IS RECORD ( field 1_name field_data_type,


field 2_name field_data_type, …

field n_name field_data_type);

identifier_name type_name;

in defining fields datatype, one can use %TYPE and %ROWTYPE attributes,
also initial (default values) could be assigned to the fields, and they could be
defined as NOT NULL fields.

EXAMPLE:-

DECLARE
TYPE user_rec IS RECORD
(deptno Number(2) := 20,
dname varchar2(20),

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
13
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

loc dept.loc%type);

my_rec user_rec;

BEGIN my_rec
my_rec.dname :=‘ACCT’;
20 ACCT

END;

ASSIGNMENT

my_rec2 :=my_rec;

This is legal only if my_rec and my_rec2 belong to the same RECORD Type,
i.e. they are declared in the following way:

DECLARE
TYPE user_rec IS RECORD
(deptno Number(2) := 20,
dname varchar2(20),
loc dept.loc%type);

my_rec user_rec;
my_rec2 user_rec;
begin

my_rec2 := my_rec;

end;

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
14
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

NESTED RECORDS ‫هون وصلت‬


Records can be nested as shown in the following example:

DECLARE
TYPE emp_rec IS RECORD
(EMPNO NUMBER(4),
SAL NUMBER(7,4),
DEPTNO NUMBER(3));

TYPE comp_rec IS RECORD


(MGR NUMBER(4),
EMP EMP_REC, -- nested record
LOC VARCHAR2(10));

SHC comp_rec; -- Record SHC of type comp_rec

BEGIN

SHC.MGR := ‘2221’;
SHC.EMP.EMPNO := 1234;
END:

One of the advantages of RECORDS is that you collect information about the
attributes of an object in one name.
Later you can refer the object as a whole. For example, you can pass the
whole RECORD to a function instead of passing the individual fields.

NESTED BLOCKS and SCOPE of VARIABLES

You can nest blocks wherever an executable statement is allowed.

The scope of a variable is the region that can refer to the variable.
You can reference the declared variable in the executable section.

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
15
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

Try the following from SQL Prompt

SQL> SET SERVEROUTPUT ON

Declare
A REAL;
Begin
A := 5.2;
DECLARE
A Real;
Begin
A := 2.0;
END;
DBMS_OUPUT.PUT_LINE (‘The variable A = ‘|| A);
END;

The variable A = 5.2.

The variable A declared in the inner block is different than the A declared in
the outer block and is local for the inner block

Note: DBMS_OUTPUT.PUT_LINE is a built in function that displays what is in the buffer.


To enable this function the environment parameter SERVEROUTPUT should be set to ON.

ASSIGNING VALUES to VARIABLES

To assign value to a variable you use ( := ) operator, which applies only to


PL/SQL but not to SQL.

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
16
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

Variable name := value ;

Example:

Var1 := 5; -- var1 is a defined variable, its datatype is number

Var2 := var1 * 3; -- var2 is a defined variable, its datatype is number

Var3 :=’ORACLE’ -- var3 is a defined variable, its datatype is char

My_tab(10).empno := 2110; /* this assigns a value of 2110 in the 10th binary


integer index record to field empno in a PL/SQL table
which is my_tab */

My_rec.dname := ‘TRAINING’ -- field dname in PL/SQL record (my_rec).

Any variable which does not have a value assigned to has an initial value of
NULL.

Example:

DECLARE
Iterate INTEGER;
BEGIN
Iterate := Iterate +1 ;
....
END:

Iterate will always be null because iterate is not initialized

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
17
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

Functions in PL/SQL

Most of the function in SQL are available in PL/SQL;

Single row functions ( numbers, character, date and data type conversion )

Group functions ( max, min, sum, … etc.) , Greatest and Least function are
not available in procedural statements.

The following statement will give an error

Var1 := max(my_new_sal);

Where my_new_sal is the name of a stored procedure.

Note:The concept of stored procedures will come later in this course.

Precedence when Evaluating Expressions

** , Not
* ,/
+ . - , ||
= , != , < , > , etc. .. Decreasing
And
Or

Comparison with NULL yields NULL;


NOT (NULL) = NULL

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
18
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

example a:= Null;


b := Null;
IF a = b then
dbms_output.put_line (‘OK’);
End IF;

The message OK will not appear because


the conditional statement will evaluate to Null

Note also that Zero Length strings are treated like null

Remember that if you wish to test for NULL use IS NULL

i.e. Select * from emp where comm IS NULL;

EXCEPTIONS:

In PL/SQL, a warning or error condition is called an exception.


Exception can be internally defined (by runtime system) or user_defined.

There are three types Exceptions in PL/SQL


i) pre-defined (internally defined) exception
ii) user defined exceptions
iii) pragma exception init

Examples of internally defined exceptions include divide


by zero and no data found. Common internal exceptions
have predefined name such as ZERO_DIVIDE and
NO_DATA_FOUND

When an error occurs, an exception is raised. That is,


Normal execution stops and control transfers to the
exception handling part of your PL/SQL Block.

To handle raised exceptions, you write separate routines


exception handlers. After an exception handler runs, the
current block stops executing.

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
19
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

Without exception handling, every time you issue a


command, you must check for execution errors

Example Using Exceptions


Declare
val number ;
Begin
select sal into val from emp where empno=9989;
if val < 800 then
Update emp set sal=1000 where empno=9989;
end if;
Exception
When NO_DATA_FOUND Then
insert into log values (9989,’no employee’);
end;
End;

PREDEFINED EXCEPTIONS

Exception Name ORACLE error SQLCODE

CURSOR_ALREADY_OPEN ORA-06511 -6511


DUP_VAL_ON_INDEX ORA-00001 -1
INVALID_NUMBER ORA-01722 -1722

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
20
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

NO_DATA_FOUND ORA-01403 +100


PROGRAM_ERROR ORA-06501 -6500
TIMEOUT_ON_RESOURCE ORA-00051 -51
TOO_MANY_ROWS ORA-01422 -1422
VALUE_ERROR ORA-06502 -6502
ZERO_DIVIDE ORA-01476 -1476

INVALID_NUMBER can, for example, be raised when you try to insert in


string into a number field in the database.

NO_DATA_FOUND is raised when SELECT INTO returns no rows or when


you reference an uninitialized row in PL/SQL table.

TIME_OUT_RESOURCE is raised when you are waiting for a locked table or


any other resource

USER DEFINED EXCEPTIONS

Define Your own EXCEPTION as Follows:

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
21
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

Syntax:

Declare
Exception_name EXCEPTION

BIGIN
Statement1;
Statement2;

if … then
raise Exception_name;
end if;
Exception
When exception_name then

END;

Example:

DECLARE
angry EXCEPTION
...
BEGIN
...
IF x > 100 then
RAISE angry;
END IF;
EXCEPTION
WHEN angry THEN
.....
WHEN OTHERS THEN
....
END ;

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
22
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

PRAGMA EXCEPTION INIT ( non predefined Oracle Server Errors )

It is also possible to handle Internal error that do not have PRE-DEFINED


EXCEPTIONS.
This is only possible if one knows the ORACLE’s Error code which
is associated with that internal error.

Syntax:

Declare

Exception_name EXCEPTION;
PRAGMA EXCEPTION_INIT(Exception_name,error number);
BEGIN
Statement1;
Statement2;

EXCEPTION
WHEN Exception_name THEN

END;

Exampe:

DECLARE
no_privileges EXCEPTION;
PRAGMA EXCEPTION_INIT(no_privileges,-1031);
-- -1031 is the error code associated with trying to update
-- a table for which you only have SELECT privileges

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
23
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

BEGIN
UPDATE team4.emp SET sal=sal+100;
EXCEPTION
WHEN no_privileges THEN
-- handle the newly defined exception
WHEN others THEN
-- Handle other exceptions
END;

RAISE_APPLICATION_ERROR

 This is a procedure which lets you issue user_Defined error


messages from your PL/SQL Code.
 Called only from an executed stored subprogram.

Example:

DECLARE
x number;
BEGIN
SELECT comm INTO x FROM emp WHERE empno= 7902;
IF x IS NULL THEN
raise_application_error(-20001,’Comm is Null’);
ELSE
UPDATE emp set comm = comm+200 where empno = 7902;
END IF;
END;

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
24
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

This procedure RAISE_APPLICATION_ERROR could be used in tow places:


 Executable part.
 Exception part.

Returns error conditions to the user in a manner consistent with other Oracle
Server errors.

Trapping Exceptions

Exceptions could be trapped by using:

 SQLCODE
This returns the numeric value for the error code.

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
25
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

 SQLERRM
This returns the message associated with the error number.

Example:

Declare
val number ;
Begin
select sal into val from emp where empno=9989;
if val < 800 then
Update emp set sal=1000 where empno=9989;
end if;
Exception
When NO_DATA_FOUND Then
declare
val1 number;
val2 varchar2(50);
begin
val1 := sqlcode;
val2 := sqlerrm;
insert into log values (val1,val2);
end;
End;

More about SQLCODE


If SQLCODE = 0, means no exception encountered.
If SQLCODE = 1, means User-defined exception
If SQLCODE = +100, NO_DATA_FOUND exception
If SQLCODE is a NEGATIVE number, means another Oracle Server Error number.

Propagating Exceptions

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
26
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

RETRYING A TRANSATION :-

Example:

DECLARE
empno number(4) := 7755;
BEGIN
FOR i in 1 .. 3 LOOP -- Retry Three Times

BEGIN
SAVEPOINT start1; -- Mark a savepoint
INSERT INTO EMP (empno,sal)
VALUES( empno,1000);

-- might raise DUP_VAL_ON_INDEX


-- if empno already exists;
COMMIT;
exit;
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN
ROLLBACK to start1; -- UNDO
empno := empno +1; -- try to fix
END;

END LOOP;
END;

CAN YOU EXPLAIN IT?

NOTE about raise_application_error: The code number you


are allowed to use is restricted to the range -20000 .. -20999

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
27
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

CONTROL STRUCTURES
IF and IF-THEN-ELSIF Statement

The use of the IF statement in PL/SQL is the same as in all other procedural
languages,

Syntax:

IF condition THEN
Statement(s);
ELSIF condition THEN
Statement(s);

ELSE
Statement(s);
END IF;

Example:

Begin

If sales > 50000 Then


bonus := 1500;

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
28
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

Elsif sales > 35000 Then


bonus := 500;
Else
bonus := 100;
End If;
Insert into jpayroll Values (emp_id,bonus ..);

End;

make note of Elsif , it does not contain the letter ‘e’ after Els, nor there is
space before if i.e. (ELSIF)

DO NOT MIS-USE IF STATEMET


AND DO NOT MAKE A SPELINLG MISTAKE WITH
IF - ELSE - ELSIF and END IF

The following is not efficiently written :-

DECLARE
overdrawn BOOLEAN;
......
BEGIN

IF new_balance < minimum_balance THEN


overdrawn := TRUE;
ELSE
overdrawn := FALSE;
END IF;

IF overdrawn = TRUE THEN


......
END IF;
END:

YOU can replace the first IF statement with

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
29
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

overdrawn := new_balance < minimum_balance;

AND the second IF with

IF overdrawn THEN

Nested IF statements:

IF v1 < 550 the


IF a<b then
Update emp set …..
Else
Insert into …..
End if;
Elsif v1 between 550 and 100 then

else delete from …
END IF;

LOOP

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
30
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

- Loops repeat a statement or sequence of statements


multiple times.

- There are three types of loop;

- Basic loops
- For loops
- While loops

1) Basic Loop

Syntax:

Loop
Statement1
Statement2
….
Exit When condition;
End loop;

Example:

Declare
Cnt number := 1;
Begin
Loop
Dbms_output.putline(to_char(x)||’ ‘||to_char(x * 9/5 + 32));
Cnt := cnt + 1;
Exit when cnt > 10;
End loop;
End;

The above example displays the degrees in C from 1 to 10 and


the corresponding values in F.

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
31
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

2) For Loop

Syntax:

For loop_counter in [ reverse ] lower_value .. upper_value loop


Statement1
Statement2
….
End loop;

Note: loop counter does not need declaration.


loop counter could be referenced only inside the loop.
Loop counter should not be target of an assignment.

Example:

For c in 1 .. 20 loop
Dbms_output.putline( c * 10 );
End loop;

The above example displays the output of multiplying the


counter ( from 1 to 20 ) times 10.

BASIC Language provides the following:

FOR I = 1 TO 100 STEP 5;

How can we implement this is PL/SQL?

There is no direct way to do it in PL/SQL but if you


use your mind, you can do the following:-

FOR i in 1..100 LOOP

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
32
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

IF MOD(i,5) = 0 THEN
sequence of statements;
END IF;
END LOOP

3) While Loop

Syntax:

While condition Loop


Statement1;
Statement2;

End loop;

The statements inside the loop are repeated until the condition is
evaluated to FALSE.

The condition is evaluated at the beginning of the loop.

Example:

Declare
Cnt number := 1;
Begin
While cnt <= 10 Loop

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
33
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

Dbms_output.putline(to_char(x)||’ ‘||to_char(x * 9/5 + 32));


Cnt := cnt + 1;
End loop;
End;

This will do the same as for the example in the Basic Loop.

NESTED LOOPS

- Loops could be nested to multiple levels


- Labels are used to distinguish between loops and blocks
- To exit the outer loop use exit with the label for that outer
loop.

Syntax:

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
34
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

Declare

begin

<< loop_1 >>
loop
v1 := v1 + 1;
exit when v1 = 25;
<< lp_2 >>

exit when x = 10; -- this will exit the inner loop(lp_2)
statement1;

exit loop_1 when count_a = 100; -- this will exit the outer loop
end loop lp_2;

end loop loop_1;
end;

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
35
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

HOW about GOTO Statement?

PL/SQL provides GOTO statement but I will not


mention it because all programming teachers say

DO NOT USE IT!


BEGIN
...
GOTO FIRST; -- The label is FIRST...
....
<<FIRST>> -- Label
INSERT INTO emp VALUES ...
END;

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
36
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

The World of Cursors


Every SQL statement executed by the Oracle Server has a cursor associated
with it.
And CURSORS are two types:
 Implicit Cursors: declared for all DML and PL/SQL select statements.
 Explicit Cursors: declared and named by the programmer.

The set of rows returned by a query can consist of zero, one, or multiple rows,
depending on your search condition.
When query return multiple rows, you can explicitly define a cursor to process
the rows.

Explicit Declaration

Steps for Explicit Cursors:

 Declare: Create a named SQL area.


 Open: Identify the active set.
 Fetch: Load the current row into variables.
 Close: Releases the active set.

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
37
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

Syntax:

Declare

CURSOR cursor_name IS select statement ;

begin

OPEN cursor_name;

FETCH cursor_name into variables;

CLOSE cursor_name;

End;

 Do not include the INTO clause in the declaration on\f the cursor.
 If the rows are to be processed in certain sequence, use the ORDER
BY clause in select statement.
 The where clause could be used in the cursor declaration.
 Open the cursor to execute the query, and identify the active set.
 No exception is raised if the query returns no rows.

- CURSOR C1 is Select id,name From mytable


where id > 2000;

This is a declaration statement of a cursor that


contains

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
38
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

a simple SELECT and must be declared in the


declaration
section of a PL/SQL Block. C1 is the name of the
cursor
and can be any name.

One can use the cursor within the body of PL/SQL


by using
OPEN, FETCH .. INTO and CLOSE

THE OPEN statement executes the query


associated with
the cursor, and identifies the active set, and
positions the
cursor at the first row. The FETCH .. INTO
statement retrieves
the current row AND stores it into local variables
and advances
the cursor to the next one. The CLOSE statement
disables
the cursor. The following diagram explains the
concept.

emp table

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
39
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

CURSOR CONTROL
emp table
7369 SMITH CLERK 800 20
7499 ALLEN SALESMAN 1600 300 30
7521 WARD SALESMAN 1250 500 30
7566 JONES MANAGER 2975 20
7654 MARTIN SALESMAN 1250 1400 30
7698 BLAKE MANAGER 2850 30
20 800
7782 CLARK CLERK
MANAGERSMITH2450 7369 10
30 300 1600
7788 SCOTT NANALYST
SALESMAN 3000
ALLEN 207499
30 500 etc1250 ....SALESMAN WARD 7521
20 CURSOR C1 IS MANAGER
2975 SELECT EMPNO,ENAME,JOB
JONES 7566 FROM
30 EMP
1400 1250 SALESMAN MARTIN 7654
30 WHERE SAL
2850 > 1250;
MANAGER BLAKE 7698
OPEN
10 2450 MANAGER CLARK 7782
.... etc
FETCH

CURSOR C1 IS SELECT EMPNO,ENAME,JOB FROM EMP


;WHERE SAL > 1250

OPEN ACTIVE SET

Current
FETCH SALESMAN ALLEN 7499 Row
MANAGER JONES 7566
Prepared By : MANAGER BLAKE 7698
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
MANAGER CLARK 7782
ANALYST 40SCOTT 7788

CLOSE
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

EXAMPLE

DECLARE
CURSOR my_cursor IS
SELECT sal+NVL(comm,0) wages , ename
FROM EMP;
X1 number;
x2 varchar2 (20);
BEGIN
OPEN my_cursor;
LOOP
FETCH my_cursor INTO x1,x2;
EXIT WHEN my_cursor%NOTFOUND;
IF x1 > 2000 THEN
INSERT INTO anytable VALUES (x1,...);

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
41
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

END IF;
END LOOP;
CLOSE my_cursor;
END;
The same thing can also be done as
follows
CURSOR my_cursor IS
SELECT sal+NVL(comm,0) wages , ename
FROM EMP;
my_rec my_cursor%ROWTYPE --
BEGIN
CONT .Next Page
OPEN my_cursor;
LOOP
FETCH my_cursor INTO my_rec;
EXIT WHEN my_cursor%NOTFOUND;
IF my_rec.wages > 2000 THEN
INSERT INTO anytable
VALUES (my_rec.sal, .);
END IF;
END LOOP;
CLOSE my_cursor;
END;

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
42
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

Passing Parameters to Cursors


When you open a cursor you can pass parameters
to it.
For example

DECLARE
CURSOR MY_C (VAR 1 NUMBER) IS
SELECT ENAME,SAL, DEPTNO FROM EMP
WHERE EMPNO=VAR1;
BEGIN
OPEN MY_C (7499);
etc.
END;

MORE EXAMPLE ON FETCHING CURSORS

DECLARE
CURSOR C1 IS SELECT
EMPNO,ENAME,SAL FROM EMP;
m_empno NUMBER(10);
m_ename VARCHAR2(10);
m_sal NUMBER(2);
BEGIN
OPEN C1; -- ACTIVE SET IS IDENTIFIED.

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
43
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

LOOP
FETCH C1 INTO
m_empno,m_ename,m_sal;
EXIT WHEN C1%NOTFOUND; -- when
EOF
IF m_sal < 2000 THEN
UPDATE EMP SET SAL=SAL*1.1
where
empno = m_empno;
END IF;
END LOOP;
CLOSE C1
END;
Note: What happens if during the fetch of a certain record whose
m_sal is <
2000, the same record was changed by another user? How to
correct the
problem

DECLARE
CURSOR C1 IS SELECT JOB FROM EMP;
JOB1 EMP.JOB%TYPE;
BEGIN
OPEN C1;
LOOP
FETCH C1 INTO JOB;
EXIT WHEN C1%NOTFOUND;
...
Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
44
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

END LOOP;
CURSOR ATTRIBUTES
%NOTFOUND
%FOUND
%ROWCOUNT
%ISOPEN
Before the first fetch, the %NOTFOUND evaluates
to NULL.
If the last fetch fails to return a row %NOTFOUND
evaluates to TRUE.

%FOUND is the opposite of %NOTFOUND.

When you open the cursor, %ROWCOUNT is


zeroed.
%ROWCOUNT is incremented every time a row is
fetched.

%ISOPEN evaluates to TRUE if its cursor is open

IF c1%ISOPEN THEN -- cursor is open


...
ELSE OPEN c1
EXAMPLE
DECLARE
num1 data_table.n1%TYPE;

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
45
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

num2 data_table.n2%TYPE;
num3 data_tablen3%TYPE;
result temp.col1%TYPE;
CURSOR c1 IS
SELECT n1,n2,n3 from data_table
WHERE experiment=1;
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO num1,num2,num3;
EXIT WHEN c1%NOTFOUND; -- UNTIL
EOF
result := num2/(num1+num3);
INSERT INTO temp VALUES (result);
END LOOP;
CLOSE c1;
COMMIT;
END;
Note the Use of COMMIT to make sure
that
the inserted records are saved, the end of
the
block does not automatically commit a
trans.

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
46
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

UPDATING THE RECORD OF THE CURRENT


CURSOR
We have seen the following example before. Is this the
best way to implement the Update?

DECLARE
CURSOR C1 IS SELECT
EMPNO,ENAME,SAL FROM EMP;
m_empno NUMBER(10);
m_ename VARCHAR2(10);
m_sal NUMBER(2);
BEGIN
OPEN C1; -- ACTIVE SET IS IDENTIFIED.
LOOP
FETCH C1 INTO m_empno,m_ename,m_sal;
EXIT WHEN C1%NOTFOUND; -- when EOF
IF m_sal < 2000 THEN
UPDATE EMP SET SAL=SAL*1.1 where
empno = m_empno;
END IF;
END LOOP;
CLOSE C1
END;

Note that the Update statement is intended to update the


record currently fetched by cursor C1. Instead of using
Where empno=m_empno , we can use FOR UPDATE

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
47
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

and CURRENT OF Construct as the following example


shows
DECLARE
CURSOR C1 IS SELECT
ENAME,SAL FROM EMP FOR
UPDATE OF SAL;
m_ename VARCHAR2(10);
m_sal NUMBER(2);
BEGIN
OPEN C1; -- ACTIVE SET IS IDENTIFIED.
LOOP
FETCH C1 INTO m_ename,m_sal;
EXIT WHEN C1%NOTFOUND; -- when
EOF
IF m_sal < 2000 THEN
UPDATE EMP SET SAL=SAL*1.1
where
CURRENT OF C1;
END IF;
END LOOP;
CLOSE C1
END;

Notes:

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
48
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

1- FOR UPDATE OF makes sure that the active


set is locked and cannot be changed by other
users.

2- CURRENT OFC1 means that the update will


affect the currently fetched record.

3- Please remember that CURRENT OF is valid


only if the cursor was declared using the FOR
UPDATE
One problem with CURRENT OF usage is that it cannot
be used if COMMIT is issued within the loop. Doing so
will result in a “FETCH OUT OF SEQUENCE” error. i.e
the following will give an error

LOOP
FETCH C1 INTO m_ename,m_sal;
EXIT WHEN C1%NOTFOUND; -- when EOF
IF m_sal < 2000 THEN
UPDATE EMP SET SAL=SAL*1.1 where
CURRENT OF C1;
END IF;
COMMIT -- This will cause an error
END LOOP;

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
49
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

Before discussing who to fix this problem. Is it good to


Commit within a loop?

Cosider the following factors before you answer:


ROLLBACK,
PERFORMANCE, MEDIA FAILURE, POWER FAILURE.

FIX:
The best fix to the above mentioned problem is not to use
the CURRENT OF construct. Instead, fetch the ROWID in
the cursor and use it in the where clause of the Update
statment to help identify the record being updated. The
usage of ROWID will guarantee the fastest access path to
your record. The following example illustrates:
DECLARE
CURSOR C1 IS SELECT
ENAME,SAL rowid FROM EMP FOR
UPDATE OF SAL;
m_ename VARCHAR2(10);
m_sal NUMBER(2);
m_rowid rowid;
BEGIN
OPEN C1; -- ACTIVE SET IS IDENTIFIED.
LOOP
FETCH C1 INTO m_ename,m_sal,m_rowid;
EXIT WHEN C1%NOTFOUND; -- when EOF
IF m_sal < 2000 THEN
UPDATE EMP SET SAL=SAL*1.1 where

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
50
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

rowid=m_rowid;
END IF;
COMMIT;
END LOOP;
CLOSE C1
END
IMPLICIT CURSORS
ORACLE implicitly opens a cursor to process each SQL
statement not associated with an explicitly declared
cursor.
You can refer to most recent implicit cursor as the “SQL”
cursor.
Example

UPDATE daily_journal SET qty = qty + 1


WHERE part_id = 100;
IF SQL%NOTFOUND THEN -- No Rows
updated
INSERT INTO Purchase_order values
(100);
END IF;
Note that SQL%NOTFOUND evaluates the LAST
SQL
statement only
DECLARE
my_count;
BEGIN

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
51
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

SELECT MAX(sal) INTO my_count FROM emp


WHERE deptno = 8898; -- CONTINUE NEXT
PAGE

IF SQL%NOTFOUND THEN
DELETE FROM EMP;
/* THIS action is never taken because
the function MAX evaluates to a value
or a NULL and %NOTFOUND evaluates
to FALSE */
END IF;
EXCEPTION
WHEN NO_DATA_FOUND THEN NULL;
-- never reaches this part.
END:
CURSOR FOR LOOP
A cursor FOR LOOP implicitly declares its Loop
index
as a record of type %ROWTYPE, OPENS a cursor,
FETCHES records and CLOSES the cursor
DECLARE
result temp.col1%TYPE;
CURSOR C1 IS SELECT n1,n2,n3 from m_tab;
BEGIN
FOR my_record IN C1 LOOP;

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
52
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

Let us explain this FOR Loop


The counter my_record can be any name. When
the FOR statement is executed, the my_record is
implicitly declared as a RECORD with fields and
datatypes similar to those existing in the CURSOR
c1. The FOR LOOP
OPENs the CURSOR c1 and FETCHEs all column
values
of the current row and stores into the record fields
which are

my_record.n1
my_record.n2
my_record.n3

This is equivalent to saying

DECLARE
CURSOR C1 IS SELECT n1,n2,n3 from m_tab;
my_record c1%ROWTYPE;
BEGIN
OPEN c1;
LOOP;
FETCH c1 into my_record.n1,
my_record_n2,my_record.n3;
EXIT WHEN C1%NOTFOUND;

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
53
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

END LOOP;
CLOSE c1
END;

PL/SQL TABLES
If you try to reference an uninitialized row in a
PL/SQL table, then NO_DATA_FOUND exception
is raised.
More examples:-

For i in 1..50 Loop


Insert into dept (deptno)
values (scottab ( i ) );
End loop;

Declare

Type tabtype IS TABLE of number;


INDEX BY BINARY_INTEGER;

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
54
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

TYPE tabtype 1 IS TABLE of CHAR(20)


INDEX BY BINARY_INTEGER;

Empno_tab tabtype;
ename_tab tabtype1;

To Delete PL/SQL table or to deinitialize one


element of
the Table, simply assign it to NULL. i.e

scottab(i) := NULL;
Example

DECLARE

TYPE my_array IS TABLE OF DATE


INDEX BY BINARY_INTEGER;

arr_null my_array;
arr1 my_array;
BEGIN

For i in 244669 .. 244699 Loop


arr1(i) := to_date(i,’j’); -- j joulean date
insert into emp (empno,hiredate,deptno)

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
55
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

values (1,arr1(i),5);
END LOOP;
arr1 := arr_null; -- delete arr1
END;

PLSQL RECORDS
You can define a record that holds the same
structure of a record in the database (but it is NOT
the same as a row in the database) as follows:-

DEF_REC DEPT%ROWTYPE;

What if you want to define a record to your own

DECLARE

TYPE user_rec IS RECORD


(deptno Number(2) := 20,
dname varchar2(20),
loc dept.loc%type);

my_rec User_rec;
my_rec2 User_rec;
BEGIN my_rec
my_rec.dname :=‘ACCT’;
END;
Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
56
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

Exercise
1-Write one SQL statement (NOT PL/SQL) that will
update the EMP table so that all employees whose salary
is more than 2000 will get 5% increase in salary and
empolyees whose salary is less or equal to 2000 will get
10% increase.

2-Try the same thing using PL/SQL?

3- Write a PL/SQL block that will convert temperature


from degrees C to degrees F. The range of temperature
values are 0-35.
SOLUTION FOR the last problem
DECLARE
y number;
BEGIN
FOR i in 1 .. 30 loop
y := (9/5)*i + 32;
DBMS_OUTPUT.PUT_LINE (i||’ C= ‘|| y||’
F’);
End loop;
END;

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
57
‫الـع ــالـــميـــة‬
‫للكمــبـيـوتـر‬

Prepared By :
Abdullah A. Safareeni Mobile: +962 79 5091611 E-Mail:
[email protected]
58

You might also like