Lab On PLSQL
Lab On PLSQL
Lab On PLSQL
II Semester 2008-09
CS C352 Database Systems
Overview of PL/SQL
PL/SQL is a block-structured language. That is, the basic units (procedures, functions, and anonymous
blocks) that make up a PL/SQL program are logical blocks, which can contain any number of nested subblocks. Typically, each logical block corresponds to a problem or sub problem to be solved. PL/SQL is not
case-sensitive, so lower-case letters are equivalent to corresponding upper-case letters except within string
and character literals.
PL/SQL provides additional capabilities that SQL lacks:
Secure code through encryption and by storing code on a server instead of a client computer.
Handle exceptions that arise due to data entry errors or programming errors.
Process record with iterative loop code that manipulates records one at a time.
Work with variables, records, arrays, objects, and other common programming language constructs.
These are the advantages of PL/SQL.
Better Application Performance: PL/SQL provides the capability to define a "block" of programming
statements, and can transmit this block to an Oracle database as a unit of work. When multiple
SELECT statements are part of a PL/SQL block, there are still only two network trips. This improves
system response time by decreasing the amount of network and performance overhead that is
incurred with an approach where each SELECT statement processes individually.
Productivity, Portability, and Security: PL/SQL stored procedures run on a server instead of a
client computer. This means that the procedures can be secured from tampering by unscrupulous
hackers by restricting access through the use of standard Oracle database security. Consider a
typical business requirement to update a customer order. Instead of granting access to the customer
order table, you can grant access to a procedure that has been coded to update the table.
PL/SQL BLOCK STRUCTURE: A PL/SQL blocks combine statements that represent a single logical task.
When the PL/SQL engine is located on the server, the entire PL/SQL block is passed to the PL/SQL engine
on the Oracle server. PL/SQL blocks can be divided into two groups: named and anonymous. Named blocks
are used when creating subroutines. Subroutines include procedures and functions. Anonymous PL/SQL
blocks, as you have probably guessed, do not have names. As a result, they cannot be stored in the
database and referenced later.
SQL>/
DECLARE
num_age
NUMBER(3) := 20; -- assign value to variable
BEGIN
num_age := 20;
DBMS_OUTPUT.PUT_LINE('My age is: ' || TO_CHAR(num_age));
END;
Declarations:
Your program stores values in variables and constants. As the program executes, the values of variables can
change, but the values of constants cannot.
Data Type
Usage
Sample Declaration
VARCHAR2
Variable-length character strings FirstName VARCHAR2(30);
CHAR
Fixed-length character strings
StudentGender CHAR(1);
NUMBER
Floating, fixed-point, or integer Salary NUMBER(6);
numbers
PLS_INTEGER
Integers
StudentID PLS_INTEGER;
(BINARY_INTEGER)
DATE
BOOLEAN
LOB
(It has four data types: BFILE,
BLOB, CLOB and NCLOB)
%TYPE
%ROWTYPE
Dates
TRUE / FALSE / NULL values
Large Objects
TodaysDate DATE;
OrderFlag BOOLEAN;
Message CLOB;
CustAddress customer.cadd%TYPE;
CustOrderRecord
%ROWTYPE;
cust_order
HIREDATE
DATE;
ROOTTWO
CONSTANT NUMBER: = 1.414
ACCT_ID
VARCHAR2 (5) NOT NULL: = 'PS001';
ingredients INGREDIENTS%ROWTYPE
foodgrp
ingredients.foodgroup%type
The first declaration names a variable of type DATE. The second declaration names a variable of
CONSTANT type NUMBER and uses the assignment operator (:=) to assign an initial value of 1.414 to the
variable. The third declaration names a variable of type VARCHAR2, specifies the NOT NULL constraint, and
assigns an initial value of 'PS001' to the variable.
EXAMPLE:
DECLARE
wages
NUMBER;
hours_worked
NUMBER := 40;
hourly_salary NUMBER := 22.50;
bonus
NUMBER := 150;
country
VARCHAR2(128);
counter
NUMBER := 0;
done
BOOLEAN;
valid_id
BOOLEAN;
TYPE commissions IS TABLE OF NUMBER INDEX BY PLS_INTEGER; --user defined type
comm_tab
commissions;
BEGIN
wages := (hours_worked * hourly_salary) + bonus;
country := 'France';
country := UPPER('Canada');
done := (counter > 100);
valid_id := TRUE;
comm_tab(5) := 20000 * 0.15;
END;
/
Error Handling: This section contains statements that are executed whenever a runtime error occurs within
the block. When a runtime error occurs, program control is passed to the exception-handling section of the
block. The runtime error is then evaluated, and a specific exception is raised or executed..
Exceptions :
Exceptions are of two types : user defined and predefined.
declare
over_withdrawal
exception; --user defined exception
begin
if creditlimit < withdrawal_amount then
raise insufficient_balance;
end if;
exception
when over_withdrawal then
insert into log values(balance insufficient);
end;
Important predefined exceptions are NO_DATA_FOUND (when the query did not return any row) and
TOO_MANY_ROWS (when the query returned more than one row). You dont have to declare these
exceptions . If you want to catch other exceptions (other predefined exceptions you dont know but you want
a default action for) then you can use the default exception handler OTHERS.
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
END;
Control Constructs :1) IF <condition> THEN <actions> [ ELSEIF <condition> THEN <actions>][ELSE
<actions>] ENDIF;
2) LOOP <sequence of statements>
IF <condition> THEN EXIT;
ENDIF;
END LOOP;
Important: there is no default exiting of loop so you must specify an explicit exit condition.
3) WHILE <condition is true> LOOP
< sequence of statement >
END LOOP;
4)
FOR <counter> IN <lower bound> <higher bound> LOOP
<sequence of statement> END LOOP
SQL> DECLARE
v_PurchaseAmount NUMBER(9,2) := 1001;
v_DiscountAmount NUMBER(9,2);
BEGIN
IF NOT (v_PurchaseAmount <= 1000) THEN
v_DiscountAmount := v_PurchaseAmount * 0.05;
END IF;
DBMS_OUTPUT.PUT_LINE('Discount: ' || TO_CHAR(v_DiscountAmount));
END;
SQL>
DECLARE
v_CustomerStatus CHAR(3) := '&CustomerStatus';
v_PurchaseAmount NUMBER(9,2) := '&PurchaseAmount';
v_DiscountAmount NUMBER(9,2);
BEGIN
IF v_CustomerStatus = 'AAA' THEN
IF v_PurchaseAmount > 1000 then
v_DiscountAmount := v_PurchaseAmount * 0.05;
ELSE
v_DiscountAmount := v_PurchaseAmount * 0.02;
END IF;
ELSE
v_DiscountAmount := 0;
END IF;
DBMS_OUTPUT.PUT_LINE('Discount: ' || TO_CHAR(v_DiscountAmount));
END;
/
LOOP:
SQL> DECLARE
v_Balance NUMBER(9,2) := 100;
BEGIN
LOOP
v_Balance := v_Balance - 15;
IF v_Balance <= 0 THEN
EXIT;
END IF;
END LOOP;
DBMS_OUTPUT.PUT_LINE('You may have paid too much.');
DBMS_OUTPUT.PUT_LINE('Ending balance: ' || TO_CHAR(v_Balance));
END;
WHILE LOOP:
SQL> DECLARE
v_Counter NUMBER := 1;
BEGIN
END;
FOR LOOP:
SQL>DECLARE
v_Rate NUMBER(5,4) := 0.06/12;
v_Periods NUMBER := 12;
v_Balance NUMBER(9,2) := 0;
BEGIN
FOR i IN 1..v_Periods LOOP
-- loop number of periods
v_Balance := v_Balance + 50;
v_Balance := v_Balance + (v_Balance * v_Rate);
DBMS_OUTPUT.PUT_LINE('Balance for Period ' || TO_CHAR(i) || '
|| TO_CHAR(v_Balance));
END LOOP;
END;
/
'
CURSORS
When Oracle processes an SQL statement as part of a PL/SQL program, it creates an area of memory
known as the context area. This area contains the information needed to process the statement. The
information includes the number of rows processed by the statement, and a pointer to the parsed
representation of the statement. Recall that parsing an SQL statement is the process whereby information is
transferred to the server, at which point the SQL statement is evaluated as being valid. In a query, the active
set refers to the rows that are returned to the client by the SQL statement. A cursor is a handle, or a pointer,
to a context area. Through use of a cursor, a PL/SQL program lets you control the context area, access the
information, and process the data rows individually. There are two types of cursors.
An implicit cursor is automatically declared by Oracle every time an SQL statement is executed.
An explicit cursor is defined by a program for any query that returns more than one row of data.
Implicit Cursors
Whenever a SQL statement is issued, the Database server opens an area of memory in which the
statement is parsed and executed. If a PL/SQL block executes a SELECT statement that returns multiple
rows, A cursor is sort of like a temporary memory file that stores and controls the rows returned by a SELECT
statement.
When the executable part of a PL/SQL block issues an SQL statement, PL/SQL creates an implicit
cursor with an identifier of SQL. PL/SQL manages this cursor for you. Likewise, PL/SQL creates implicit
cursors for all SQL data manipulation statements (INSERT, UPDATE, and DELETE). You do not need to do
anything to handle these cursors, and you can use the attributes of implicit cursors to access information
about a cursor. Implicit cursor attributes store information about DML and DDL statements. The cursor
attributes of a SQL statement are given in Table B
Table B
%ROWCOUNT
%FOUND
%NOTFOUND
%ISOPEN
When its cursor or cursor variable is opened, %ROWCOUNT has a value zero.
Before the fetch of the first row, %ROWCOUNT is equal to zero; thereafter, it equates
to the number of rows fetched thus far by Oracle processing. The numeric value of
%ROWCOUNT is incremented if the most recent fetch returns a row.
The attribute is TRUE when a cursor has remaining rows to fetch, and FALSE when a
cursor has no rows left to fetch. The attribute is NULL until a DML statement is
executed. The attribute is TRUE if an INSERT, UPDATE, DELETE, or SELECT INTO
statement affected or returned one or more rows.
The attribute is TRUE if a cursor has no rows to fetch, and FALSE when a cursor has
some remaining rows to fetch.
The attribute is TRUE if a cursor is open, or FALSE if cursor has not been opened or
has been closed. This attribute is only used with explicit cursors.
EXAMPLE:
The program makes use of the SQL%FOUND and SQL%ROWCOUNT attributes. This example updates
unitprice for a given ingredient id.
SQL>
DECLARE
unit_priceingredients_new.unitprice%type;
old_priceingredients_new.unitprice%type;
ingredientingredients_new.ingredientid%type:='&ingredient';
BEGIN
selectunitpriceintounit_pricefromingredients_newwhereingredientid=ingredient;
IFSQL%NOTFOUNDTHEN
DBMS_OUTPUT.PUT_LINE('Ingredient'||ingredient||'notfound');
else
old_price:=unit_price;
unit_price:=unit_price+unit_price*0.1;
UPDATEingredients_newsetunitprice=unit_pricewhereingredientid=ingredient;
IFSQL%FOUNDTHENingredientupdated
DBMS_OUTPUT.PUT_LINE('Updatedunitpricefor'||ingredient||'from'||
TO_CHAR(old_price)||'to'||TO_CHAR(unit_price));
ENDIF;
ENDIF;
EXCEPTION
WHENNO_DATA_FOUNDTHEN
DBMS_OUTPUT.PUT_LINE('noingredientfound');
WHENTOO_MANY_ROWSTHEN
DBMS_OUTPUT.PUT_LINE('Toomanyrowsselectd');
END;
/
EXAMPLE:
This example updates deletes all ingredients except WATER. The success of this is reported using attributes.
The program makes use of the SQL%FOUND and SQL%ROWCOUNT attributes.
SQL>
BEGIN
DELETEFROMingredients_newWHEREingredientid<>'WATER';
IFSQL%FOUNDTHENingredientsweredeleted
DBMS_OUTPUT.PUT_LINE('Numberdeleted:'||TO_CHAR(SQL%ROWCOUNT));
ENDIF;
END;
/
Explicit Cursors
The set of rows returned by a query often includes many rows. When a query returns more than one
row, you need to declare an explicit cursor in order to process the rows. Declare cursors in the DECLARE
section of a PL/SQL block program. Explicit cursors are processed by following a four-step model.
1) First, you must declare a cursor. The general format of this declaration is:
CURSOR<cursorname>IS<SELECTstatement>;
2) The second step in using a cursor is to open it. When you open a cursor, the SQL statement associated
with it is parsed (checked for syntax errors).
OPEN<cursorname>;
3) Now you are ready to execute a FETCH statement. A FETCH statement actually retrieves the result set
produced by a cursors associated SELECT query. This loads the row addressed by the cursor pointer into
variables and moves the cursor pointer to the next row so that it is ready for the next fetch.
FETCH<cursorname>INTO<recordvariable(s)>;
4) Finally, a cursor is disabled with the CLOSE statement. This also causes the result set to become
undefined. A closed cursor cannot be reopened. The general format of the CLOSE statement is:
CLOSE<cursorname>;