Dso23bt sfw20bt Notes 22020
Dso23bt sfw20bt Notes 22020
PL/SQL NOTES
DSO23BT/SFW20BT
A successful executed block is the one without unhandled errors or compile errors, message
output be as follows:
PL/SQL procedure successfully completed.
• Block Types
A PL/SQL program comprises one or more blocks. The blocks can be entirely separate or
nested one within another.
One block can represent a small part of another block, which in turn can be part of the whole
unit of code.
DSO23BT/SFW20BT @22020/21 1
• Use of Variables
• Types of Variables
a) Scalar – this data types hold a single value. (data types that corresponds with column
types.
b) Composite – they allow groups of fields to be defined and manipulated in blocks.
c) References – they hold values, called pointers, but designate other program items.
DSO23BT/SFW20BT @22020/21 2
DECLARE
v_name VARCHAR2(20); VARCHAR2 is a variable-length character data. No default size.
v_initials CHAR(2); CHAR is a fixed-length character data. The length is up to 9.
v_hiredate DATE; it accepts a value in the format of DD/MM/YY
v_custno NUMBER(5); this number data type has only precision.
v_salary NUMBER(7,2); this number data type has a precision and scale.
v_answer BOOLEAN; this data type accepts one of the two values YES/NO
All variables declare here have a v_ as a prefix except constant variables which has a
prefix c_
BEGIN
SELECT columns
INTO variables
FROM tables
WHERE condition using substitution
AND another condition;
END;
/
VARCHAR2(30)
VARIABLE g_binding NUMBER prefix of binding variable is g.
DEFINE p_height = 23
VARIABLE g_area NUMBER
VARIABLE g_length NUMBER
VARIABLE g_width NUMBER
BEGIN
:g_length := &length;
:g_width := &width;
:g_area := :g_length * :g_width * &p_height;
END;
/
PRINT g_area – the print cannot be used inside the PL/SQL block.
G_AREA
----------
920
DSO23BT/SFW20BT @22020/21 3
• Declaring and initializing PL/SQL Variables
DECLARE The value to be used which can change.
v_hiredate DATE DEFAULT SYSDATE; (today’s date accepted)
v_count NUMBER(2) NOT NULL :=1;
v_emp_name VARCHAR2(14) := ‘&employee_name’;
c_tax_rate CONSTANT NUMBER(2,3) := 0.15; (initializing tax)
v_valid BOOLEAN NOT NULL := FALSE;
v_salary NUMBER(8,2) :=0;
BEGIN
END;
Rather than hardcoding the data type e.g., empname VARCHAR2(14), you can use the %TYPE
attribute to declare a variable according to another database columns. The attribute gives the
variables the data type and length of the column specified in the declaration.
DECLARE
v_hiredate emp.hiredate%TYPE := sysdate + 7; (today’s plus or minus 7 days)
v_count NUMBER(2) NOT NULL :=1;
v_emp_name emp.ename%TYPE;
v_emp_no emp.empno%TYPE := &employeeno;
c_tax_rate CONSTANT NUMBER(2,3) := 0.15; (initializing tax)
v_valid BOOLEAN NOT NULL := FALSE;
v_salary emp.sal%TYPE :=0;
BEGIN
SELECT ename,hiredate,sal
INTO v_emp_name,v_hiredate, v_salary – your SELECT statement must read INTO
FROM emp
WHERE empno = v_emp_no;
Works as a tab
DBMS_OUTPUT.PUT_LINE(v_emp_name||CHR(9)||v_hiredate||CHR(9)||
TO_CHAR(v_salary,’L99,999.99’));
The database’s DBMS is used to display individual variables with headings within
the executable area, because it can be used within the PL/SQL block
END;
Blocks cannot retrieve more than one row or records but only a single row or record. It
retrieves an error if it encountered this error, the same when it cannot retrieve any row or
records, in this instance your linking of primary and foreign keys from different tables might
not corresponds or field being tested might be incorrectly tested.
Using the DBMS_OUTPUT.PUT_LINE, you have to declare a host variable that will then
enable to display the content of the block. The declaration must be done outside the block
in this format SET SERVEROUT ON
DSO23BT/SFW20BT @22020/21 4
CHAPTER 2
Writing Executable Statements
DSO23BT/SFW20BT @22020/21 5
v. Should not be reserved words such as SELECT, FROM, WHERE, etc using in the
executions of block statements.
− Literals
− Comments
VARIABLE g_monthly_sal NUMBER
DECLARE
v_salary NUMBER(9,2);
BEGIN
:g_monthly_sal := &month_salary; --prompted to enter monthly salary
/* Compute the annual salary based on the monthly
salary input from the user */
v_salary := :g_monthly_sal * 12;
END;
/
/* and */ it is a comment that spans several lines.
-- the comment is on a single line.
• Data Type Conversion
DSO23BT/SFW20BT @22020/21 6
CHAPTER 3
Interacting with The Oracle Server
DECLARE
v_empname emp.ename%TYPE;
v_job_title emp.job%TYPE;
v_salary emp.sal%TYPE;
v_deptname dept.dname%TYPE;
BEGIN
SELECT ename,job,sal,dname
INTO v_empname,v_job_title,v_salary,v_deptname
FROM emp e, dept d
WHERE e.empno=&employeeno – the prompted value must be linked the correct column
AND e.deptno = d.deptno;/*querying from one table means you must be able to relate
them according to their relationship link if you do it incorrectly it will return no rows.*/
DBMS_OUTPUT.PUT_LINE(INITCAP(v_empname)||CHR(9)||
INITCAP(v_job_title)||CHR(9)||TO_CHAR(v_salary,’L99,999.99’)||CHR(9)||
INITCAP(v_deptname) );
END;
/
DSO23BT/SFW20BT @22020/21 7
The block below retrieves the details of an employee when the user is prompted to enter the
employee name, the substitution is done in the declaration area so that the value received
could be used throughout the block. The substitution value is converted into UPPER case so
that it corresponds with the value in the database table;
DECLARE
v_empname emp.ename%TYPE:=UPPER(’&employee_name’);
v_job_title emp.job%TYPE;
v_salary emp.sal%TYPE;
v_deptname dept.dname%TYPE;
BEGIN
SELECT ename,job,sal,dname
INTO v_empname,v_job_title,v_salary,v_deptname
FROM emp e, dept d
WHERE e.empno=&v_empname – the prompted value must be linked the correct column
AND e.deptno = d.deptno; /*querying from one table means you must be able to relate
them according to their relationship link if you do it incorrectly it will return no rows.*/
DBMS_OUTPUT.PUT_LINE(INITCAP(v_empname)||CHR(9)||
INITCAP(v_job_title)||CHR(9)||TO_CHAR(v_salary,’L99,999.99’)||CHR(9)||
INITCAP(v_deptname) );
END;
/
DECLARE
v_deptname dept.dname%TYPE;
v_job_title emp.job%TYPE:=UPPER(’&Job_title’);
v_tot_employ NUMBER(3);
BEGIN
SELECT dname, COUNT(job) job
INTO v_deptname, v_tot_employ
FROM emp e, dept d
WHERE e.empno=v_job_title– the prompted value must be linked the correct column
AND e.deptno = d.deptno;/*querying from one table means you must be able to relate
them according to their relationship link if you do it incorrectly it will return no rows.*/
DBMS_OUTPUT.PUT_LINE()
END;
DSO23BT/SFW20BT @22020/21 8
• Manipulating Data Using PL/SQL
The block below retrieves manipulate data in the database by either ADDING or UPDATING or
DELETING a row or rows using a substitute:
DECLARE
BEGIN
INSERT INTO emp VALUES(9000,’JOHN’,’MANGER’,7788,TO_DATE(sysdate,’DD-MON-
RR’),2500,NULL,40);
OR
UPDATE emp
SET sal = sal * .10
WHERE job IN(‘CLERK’,’SALESMAN’);
OR
DELETE emp
WHERE job = ‘CLERK’;
END;
/
DSO23BT/SFW20BT @22020/21 9
CHAPTER 4
Writing Control Structures
The block below retrieves the details of an employee when the user is prompted to enter the
employee name, the record retrieved is tested as to whether the departno is 20 and job is
CLERK. This IF statement has only one option to choose from.
DECLARE
v_empname emp.ename%TYPE:=UPPER(‘&employee_name’);
v_job_title emp.job%TYPE;
v_salary emp.sal%TYPE;
v_deptno emp.deptno%TYPE;
v_deptname dept.dname%TYPE;
v_new_salary emp.sal%TYPE;
BEGIN
SELECT ename,job,sal,e.deptno,dname
INTO v_empname,v_job_title,v_salary,v_deptno,v_deptname
FROM emp e, dept d
WHERE e.ename=v_empname
AND e.deptno = d.deptno;
DSO23BT/SFW20BT @22020/21 10
Enter value for employee_name: allen
The block below retrieves the details of an employee when the user is prompted to enter the
employee name, the record retrieved is tested as to whether the job is an Analyst. For an
Analyst salary has to be increased by 7.5% and the rest it must be increased by 5%.
This IF statement has two options to choose from.
DECLARE
v_empname emp.ename%TYPE:=UPPER(‘&Employ_name’);
v_job_title emp.job%TYPE;
v_salary emp.sal%TYPE;
v_new_salary emp.sal%TYPE;
BEGIN
SELECT ename,job,sal
INTO v_empname,v_job_title,v_salary
FROM emp e
WHERE e.ename=&v_empname ;
DSO23BT/SFW20BT @22020/21 11
Enter value for employee_name: adams
Employee : ADAMS employed as CLERK earns R1,100. before it has been increased by 5% to
R1,155.
DECLARE
v_empname emp.ename%TYPE:=UPPER(‘&Employ_name’);
v_job_title emp.job%TYPE;
v_salary emp.sal%TYPE;
v_new_salary emp.sal%TYPE;
BEGIN
SELECT ename,job,sal
INTO v_empname,v_job_title,v_salary
FROM emp e
WHERE e.ename=&v_empname;
• CASE expression
The CASE expression selects a result and return it. The PL/SQL CASE is little different to
the SQL one. If the value of the selector is equals to the value of the WHEN clause
expression, the WHEN clause will be executed. Few examples are shown below:
The block below retrieves the salary using the value the user is prompted to enter. The user is
prompted to enter job_title. Then block test as to whether the job retrieved is an Analyst or
Manager or Clerk. For an Analyst, the salary has been increased by 5.5%, a Manager will have
a salary increased by 5%, whereas the salary of the Clerk will increase by 7.5% and the rest of
the employees will have salary increase of 8%.
The CASE expression evaluates the value of the v_new_salary variable based on the value
of the v_job_title value.
Example 1:
DECLARE
v_job_title emp.job%TYPE:=INITCAP (‘&job_title’);
v_tot_salary emp.sal%TYPE;
v_new_salary emp.sal%TYPE;
v_count NUMBER(3);
BEGIN
SELECT COUNT(*) total,SUM(sal) tot_salary
INTO v_count,v_tot_salary
FROM emp
WHERE INITCAP(e.job)=v_job_title ;
v_new_salary:=
CASE (v_job_title)
WHEN ‘Analyst’ THEN v_new_salary * 1.055
WHEN ‘Manager’ THEN v_new_salary * 1.05
WHEN ‘Clerk’ THEN v_new_salary * 1.075
ELSE v_new_salary * 1.08
END;
DBMS_OUTPUT.PUT_LINE(‘The job title ‘||v_job_title||’ has ‘||v_count||’ pays a total
salary of ‘|| TO_CHAR(v_tot_salary,’fmL99,990.99’)||’ before it was increased to ‘ ||
TO_CHAR(v_salary,’fmL99,990.99’));
END;
/
DSO23BT/SFW20BT @22020/21 13
Example 2:
DECLARE
v_job_title emp.job%TYPE:=INITCAP (‘&job_title’);
v_tot_salary emp.sal%TYPE;
v_new_salary emp.sal%TYPE;
v_count NUMBER(3);
BEGIN
SELECT COUNT(*) total,SUM(sal) tot_salary
INTO v_count,v_tot_salary
FROM emp e
WHERE INITCAP(e.job)=v_job_title;
CASE INITCAP(v_job_title)
WHEN ‘Analyst’ THEN v_new_salary:= v_tot_salary * 1.055
WHEN ‘Manager’ THEN v_new_salary:= v_tot_salary * 1.05
WHEN ‘Clerk’ THEN v_new_salary:= v_tot_salary * 1.075
ELSE v_new_salary:= v_tot_salary * 1.08
END;
DSO23BT/SFW20BT @22020/21 14
Example 3:
DECLARE
v_job_title emp.job%TYPE:= INITCAP(‘&job_title’);
v_tot_salary emp.sal%TYPE;
v_new_salary emp.sal%TYPE;
v_count NUMBER(3);
BEGIN
SELECT COUNT(*) total,SUM(sal) tot_salary
INTO v_count,v_tot_salary
FROM emp e
WHERE INITCAP(e.job)=v_job_title ;
CASE
WHEN INITCAP(v_job_title) :=‘Analyst’ THEN v_new_salary:= v_tot_salary * 1.055
WHEN INITCAP(v_job_title) :=‘Manager’ THEN v_new_salary:= v_tot_salary * 1.05
WHEN INITCAP(v_job_title) :=‘Clerk’ THEN v_new_salary:= v_tot_salary * 1.075
ELSE v_new_salary:= v_tot_salary * 1.08
END;
i. Basic Loops
It allows execution of its statements at least one, even if the condition has been met
upon entering the loop. It initials counter to 1 before execution, then the counter will
increase within the loop and tested the end of the loop if it has met the required
condition, if so the loop with stop execution. Use this loop when the statements inside
the loop must execute at least once. Do not forget to include the EXIT statement,
because if omitted your loop will continue ENDLESSLY.
E.g., the expression below Adds a record twice into a database using a BASIC loop. The
loop test if the condition is met or not at its EXIT not at its beginning. It uses the counter
to test if the loop has reached the number iteration it is supposed to meet.
DSO23BT/SFW20BT @22020/21 15
DECLARE
v_deptno dept.deptno%TYPE;
v_deptname dept.dename%TYPE;
v_location dept.loc%TYPE :=’BOSTON’;
v_counter NUMBER(2) := 1;
BEGIN
SELECT deptno,dname,loc
INTO v_deptno,v_deptname,v_location
FROM dept
WHERE loc=v_location;
LOOP
INSERT INTO dept VALUES(v_deptno + (v_counter * 10), v_deptname,
v_location);
v_counter:= v_counter + 1;
EXIT WHEN v_counter > 3; --the loop will execute until counter is 4
END LOOP;
END;
/
With each iteration through the WHILE loop, counter which was initialized to the value
of one before the loop was executed is incremented by a specific value as long as its
value is within the condition in the WHILE clause but if the value is greater than the
condition the loop will stop executing. In this loop the test is done at the beginning of
the loop. Use this loop when you want to repeat a sequence of statements until the
controlling conditions is no longer TRUE.
E.g., the expression below Adds a record twice into a database. The loop test if the
condition is met or not at the beginning of the WHILE loop not at its end. It uses the
counter to test if the loop has reached the number iteration it is supposed to meet.
DSO23BT/SFW20BT @22020/21 16
DECLARE
v_deptno dept.deptno%TYPE;
v_deptname dept.dename%TYPE;
v_location dept.loc%TYPE :=’BOSTON’;
v_counter NUMBER(2) := 1;
BEGIN
SELECT deptno,dname,loc
INTO v_deptno,v_deptname,v_location
FROM dept
WHERE loc=v_location;
With the FOR loop counter is not declared just like we do in the other two iteration
structures, it is declared in the PL/SQL server. Its value increases or decreases(only if
the REVERSE word is used). The FOR loop has a lower counter value and upper counter
value for the range to be successful. Use the FOR loop if the number iterations are
known.
E.g., the expression below Adds a record twice into a database. The loop test if the
condition is met or not at the beginning of the FOR loop not at its end. It implicitly
declares the counter and uses the starting and ending values provided by the designer.
DSO23BT/SFW20BT @22020/21 17
DECLARE
v_deptno dept.deptno%TYPE;
v_deptname dept.dename%TYPE;
v_location dept.loc%TYPE :=’BOSTON’;
v_counter NUMBER(2) := 1;
BEGIN
SELECT deptno,dname,loc
INTO v_deptno,v_deptname,v_location
FROM dept
WHERE loc=v_location ;
FOR v_counter IN 1..3 LOOP --the loop will execute until counter is 3
INSERT INTO dept VALUES(v_deptno + (v_counter * 10), v_deptname,
v_location);
END LOOP;
END;
/
DSO23BT/SFW20BT @22020/21 18
CHAPTER 5
Working with Composite Data Types
This semester we will deal with the first type of composite data types, which is the records.
Like scalar variables, composite variables have a data type. RECORDS are used to treat related but
not similar data as a unit. They are groups of related data items stored as fields, each with its own
name and data type, separated by a comma and a semi-colon is used to terminate the record.
Records have similar features as an SQL table, the difference is the heading of each.
Creating a PL/SQL record will follow this declaration. Let us say we are required to create a record
type as it is well-known, of all employees who earns salary more than R1700 by prompting the user
to enter employee#, then a block will test if the employee earns that salary before displaying the
details. The record must store the employee’s number, name, job_title, salary and annul salary.
DSO23BT/SFW20BT @22020/21 19
DECLARE
TYPE employee_salary_type IS RECORD cannot be used to retrieve values.
(staffno NUMBER(4),
emp_name VARCHAR(13),
job_title VARCHAR(15),
salary NUMBER(8,2),
ann_salary NUMBER(10,2));
v_empno emp.empno%TYPE:=&employee_no;
--- declare a record below to take the structure of the record_type above
employ_sal_rec employee_salary_type;
BEGIN
SELECT empno,ename,job,sal, sal * 12
INTO employ_sal_rec
FROM emp
WHERE empno = v_empno;
The %ROWTYPE attribute declare a variable according to the collection of columns in the
database table or view. Fields in the record take the names and data types from the columns
of the table or view.
DECLARE
v_empno emp.empno%TYPE:=&employee_no;
v_ann_salary NUMBER(10,2);
--- declare a record below to take the structure using a specific table
employ_sal_rec emp%ROWTYPE;
BEGIN
SELECT empno,ename,job,sal, sal * 12
INTO employ_sal_rec
FROM emp
WHERE empno = v_empno;
DSO23BT/SFW20BT @22020/21 20
IF employ_sal_rec.sal > 1700 THEN
v_ann_salary := employ_sal_rec.sal * 12;
DBMS_OUTPUT.PUT_LINE(‘Employee # : ‘||employ_sal_rec.empno);
DBMS_OUTPUT.PUT_LINE(‘Employee Name: ‘|| INITCAP(employ_sal_rec.ename);
DBMS_OUTPUT.PUT_LINE(‘Job Title : ‘||INITCAP(employ_sal_rec.job);
DBMS_OUTPUT.PUT_LINE(‘Monthly Salary : ‘||
TO_CHAR(employ_sal_rec.sal,’L999,999.99’);
DBMS_OUTPUT.PUT_LINE(‘Annual Salary : ‘|| v_ann_salary,’L999,999.99’));
END IF;
END;
/
DSO23BT/SFW20BT @22020/21 21
CHAPTER 6
Writing Explicit Cursors
An explicit cursor is declared and named by the designer whereas the implicit cursor is declared by
PL/SQL internally for all DML(Data Manipulation Language).
A cursor works the same as the pointers in C++ as the point to the selected rows or records that
must be retrieved by a programming code.
a) Explicit cursors
Explicit cursor is used to individually process each row returned by a multiple-row SELECT
statement.
The set of rows returned by the multi-row query is called the active set.
The procedure of write a cursor are as follow:
DECLARE
Declare the cursor here using the SELECT statement without using INTO just like
when creat a view.
Declare either variables or record variables to accept the values from the cursor.
BEGIN
Open the cursor or test if the cursor is implicitly open.
Display all the headings here.
Loop
Fetch the values from the cursor into individual variables or record variable.
Exit the loop code;
End Loop;
Close the cursor;
END;
An example below is the cursor named emp_hiredate_cur that prompt the user to enter the
employee name and then retrieve employee no, employee name, hire date, job title, salary,
DSO23BT/SFW20BT @22020/21 22
experience in years for all employees hired after the entered employee name. Calculate and
display the total employees retrieved without using %ROWCOUNT attribute.
DECLARE
v_emp_no emp.empno%TYPE;
v_emp_name emp.ename%TYPE:=UPPER(‘&employeename’);
v_hiredate DATE;
v_ jobtitle emp.job%TYPE;
v_salary emp.sal%TYPE;
v_experience NUMBER(3):=0;
v_count NUMBER(3):=0;
CURSOR emp_hiredate_cur IS
SELECT empno,ename,hiredate,job,sal, TO_CHAR(sysdate,’yyyy’) –
TO_CHAR(hiredate,’yyyy’) experience
FROM emp
WHERE hiredate > ALL (SELECT hiredate
FROM emp
WHERE ename = v_emp_name);
BEGIN
OPEN emp_hiredate_cur;
--- headings must be displayed outside the Loop to avoid displaying them more than
once.
DBMS_OUTPUT.PUT_LINE(‘Emp No’||CHR(9)||’Emp Name’||CHR(9)||’Hire Date’||
CHR(9)||’Job Title’||CHR(9)||’Salary’||CHR(9)||’Experience’);
DBMS_OUTPUT.PUT_LINE(‘------'||CHR(9)||’---------’||CHR(9)||’-----------------------'||
CHR(9)||’---------’||CHR(9)||’---------------’||CHR(9)||’------------’);
LOOP
FETCH emp_hiredate_cur INTO v_emp_no,v_emp_name,v_hiredate,v_jobtitle,
v_salary,v_experience;
DBMS_OUTPUT.PUT_LINE(v_emp_no ||CHR(9)|| v_emp_name ||CHR(9)||
hiredate||CHR(9)||v_jobtitle||CHR(9)||v_salary||CHR(9)|| v_experience);
v_count := v_count + 1;
EXIT WHEN emp_hiredate_cur%NOTFOUND; --stop FETCH when no records exist
END LOOP;
DBMS_OUTPUT.PUT_LINE(‘Total employees retrieved : ‘||v_count); display outside
the loop
CLOSE emp_hiredate_cur;
END;
/
DSO23BT/SFW20BT @22020/21 23
• Cursor and Records
i. Declared Record
DECLARE
TYPE staff_hiredate_type IS RECORD cannot be used to retrieve values.
(staffno NUMBER(4),
emp_name VARCHAR(13),
hiredate DATE,
job_title VARCHAR(15),
salary NUMBER(8,2),
experience NUMBER(2));
staff_rec staff_hiredate_type;
CURSOR emp_hiredate_cur IS
SELECT empno,ename,hiredate,job,sal, TO_CHAR(sysdate,’yyyy’) –
TO_CHAR(hiredate,’yyyy’) experience
FROM emp
WHERE hiredate > ALL (SELECT hiredate
FROM emp
WHERE ename = v_emp_name);
BEGIN
OPEN emp_hiredate_cur;
--- headings must be displayed outside the Loop to avoid repetition
DBMS_OUTPUT.PUT_LINE(‘Emp No’||CHR(9)||’Emp Name’||CHR(9)||’Hire Date’||
CHR(9)||’Job Title’||CHR(9)||’Salary’||CHR(9)||’Experience’);
DBMS_OUTPUT.PUT_LINE(‘------'||CHR(9)||’---------’||CHR(9)||’-----------------------'||
CHR(9)||’---------’||CHR(9)||’---------------’||CHR(9)||’------------’);
LOOP
FETCH emp_hiredate_cur INTO staff_rec;
DBMS_OUTPUT.PUT_LINE(staff_rec.staffno ||CHR(9)||staff_rec. emp_name
||CHR(9)|| staff_rec.hiredate||CHR(9)||staff_rec. job_title||CHR(9)||
staff_rec. salary||CHR(9)||staff_rec. experience);
v_count := v_count + 1;
EXIT WHEN emp_hiredate_cur%NOTFOUND; --stop FETCH when no records exist!
END LOOP;
DBMS_OUTPUT.PUT_LINE(‘Total employees retrieved : ‘||v_count);
CLOSE emp_hiredate_cur;
END;
/
DSO23BT/SFW20BT @22020/21 24
ii. Record derived from Cursor
DECLARE
v_emp_name emp.ename%TYPE :=UPPER(‘&employee_name’);
v_count NUMBER(3):=0;
CURSOR emp_hiredate_cur IS
SELECT empno,ename,hiredate,job,sal, TO_CHAR(sysdate,’yyyy’) –
TO_CHAR(hiredate,’yyyy’) experience
FROM emp
WHERE hiredate > ALL (SELECT hiredate
FROM emp
WHERE ename = v_emp_name);
staff_rec emp_hiredate_cur%ROWTYPE;
BEGIN
IF NOT(emp_hiredate_cur%ISOPEN) THEN
OPEN emp_hiredate_cur; --- test if cursor is opened if not open it
END IF;
--- headings must be displayed outside the Loop to avoid repetition
DBMS_OUTPUT.PUT_LINE(‘Emp No’||CHR(9)||’Emp Name’||CHR(9)||’Hire Date’||
CHR(9)||’Job Title’||CHR(9)||’Salary’||CHR(9)||’Experience’);
DBMS_OUTPUT.PUT_LINE(‘------'||CHR(9)||’---------’||CHR(9)||’-----------------------'||
CHR(9)||’---------’||CHR(9)||’---------------’||CHR(9)||’------------’);
LOOP
FETCH emp_hiredate_cur INTO staff_rec;
DBMS_OUTPUT.PUT_LINE(staff_rec.empno ||CHR(9)||staff_rec.ename
||CHR(9)|| staff_rec.hiredate||CHR(9)||staff_rec.job||CHR(9)||
staff_rec.sal||CHR(9)||staff_rec.experience);
DSO23BT/SFW20BT @22020/21 25
Enter value for employeename: king
Emp No Emp Name Hire Date Job Title Salary Experience
---------- --------------- ----------------- --------------- ----------------- ----------------
7788 SCOTT 09/DEC/82 ANALYST 3000 39
7876 ADAMS 12/JAN/83 CLERK 1100 38
7900 JAMES 03/DEC/81 CLERK 950 40
7902 FORD 03/DEC/81 ANALYST 3000 40
7934 MILLER 23/JAN/82 CLERK 1300 39
b) Implicit cursors
DECLARE
v_emp_name emp.ename%TYPE :=UPPER(‘&employee_name’);
v_count NUMBER(3):=0;
CURSOR emp_hiredate_cur IS
SELECT empno,ename,hiredate,job,sal, TO_CHAR(sysdate,’yyyy’) –
TO_CHAR(hiredate,’yyyy’) experience
FROM emp
WHERE hiredate > ALL (SELECT hiredate
FROM emp
WHERE ename = v_emp_name);
BEGIN
--- No explicit Open
DBMS_OUTPUT.PUT_LINE(‘Emp No’||CHR(9)||’Emp Name’||CHR(9)||’Hire Date’||
CHR(9)||’Job Title’||CHR(9)||’Salary’||CHR(9)||’Experience’);
DBMS_OUTPUT.PUT_LINE(‘------'||CHR(9)||’---------’||CHR(9)||’-----------------------'||
CHR(9)||’---------’||CHR(9)||’---------------’||CHR(9)||’------------’);
FOR staff_rec IN emp_hiredate_cur LOOP
--- implicit Open and Fetch
DBMS_OUTPUT.PUT_LINE(staff_rec.empno||CHR(9)||staff_rec.ename||CHR(9)||
staff_rec.hiredate||CHR(9)||staff_rec.job||CHR(9)||staff_rec.sal||CHR(9)||
staff_rec.experience);
END LOOP;
v_count:=emp_hiredate_cur%ROWCOUNT;
DBMS_OUTPUT.PUT_LINE(‘Total employees retrieved : ‘||v_count);
END;
/
DSO23BT/SFW20BT @22020/21 26
CHAPTER 7
Advanced Explicit Cursors Concepts
Each parameter in the cursor declaration must have a corresponding actual parameter in the
OPEN statement. Parameter data types are the same as those for scalar variables, but you
do not mention their data type size. The parameter names are used for references purposes
in the SELECT statement of the cursor.
The code or expression below passes two parameters, job and deptno to a cursor named
emp_salary_cur to retrieve the employee’s number, name, salary, department name and
location. Using %ROWCOUNT get the number of records the cursor has retrieved.
DSO23BT/SFW20BT @22020/21 27
The OPEN clause of the cursor might use entered values or prompted values to execute the
cursor.
DECLARE
CURSOR emp_salary_cur(p_job VARCHAR2, p_deptno NUMBER) IS Parameters
SELECT empno,ename,sal,dname,loc
FROM emp e, dept d
WHERE job=UPPER(p_job) Parameters passed to the query.
AND e.deptno=p_deptno
AND d.deptno = e.deptno;
emp_sal_rec emp_salary_cur%ROWTYPE;
v_count NUMBER(3);
BEGIN
OPEN emp_salary_cur('&job_title',&deptno); -- OR OPEN emp_salary_cur('Salesman',30);
--- Values accepted by each parameter and passed to the SELECT statement
DBMS_OUTPUT.PUT_LINE('Emp No'||CHR(9)||'Emp Name'||CHR(9)||'Salary'||CHR(9)
||'Dept Name'||CHR(9)||'Location');
DBMS_OUTPUT.PUT_LINE('------'||CHR(9)||'---------'||CHR(9)||'---------------'||CHR(9)||
'----------------'||CHR(9)||'---------------');
LOOP
FETCH emp_salary_cur INTO emp_sal_rec;
EXIT WHEN emp_salary_cur%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(emp_sal_rec.empno||CHR(9)||INITCAP(emp_sal_rec.ename)
||CHR(9)||TO_CHAR(emp_sal_rec.sal,'l99,999.99')||CHR(9)||
INITCAP(emp_sal_rec.dname) ||CHR(9)||INITCAP(emp_sal_rec.loc));
END LOOP;
v_count:= emp_salary_cur%ROWCOUNT;
DBMS_OUTPUT.PUT_LINE('Total employees retrieved is : '||v_count);
CLOSE emp_salary_cur;
END;
/
DSO23BT/SFW20BT @22020/21 28
Enter value for job_title: clerk
Enter value for deptno: 20
Emp No Emp Name Salary Dept Name Location
----------- --------------- ----------------------- ---------------- ---------------
7876 Adams R1,100.00 Research Dallas
7369 Smith R800.00 Research Dallas
Total employees retrieved is : 2
This clause is used to lock rows that are supposed to be UPDATED or DELETED. It is added in
the cursor query to lock the affected rows when the cursor is opened.
It must be the last clause in the SELECT statement.
The optional NOWAIT keyword tells Oracle not to wait if requested rows have been locked by
another user for UPDATE.
The WHERE CURRENT OF clause is used to reference the current row from an explicit cursor.
This allows the code to apply Updates or Deletes to the row currently being addressed.
In the expression below the code goes through the employee table to check as to whether an
employee(s) who were hired after the employee’s whose name was entered by the user earns
a salary less than the R1800, if their salary is less than it must be increased by 6.5%.
DSO23BT/SFW20BT @22020/21 29
DECLARE
v_emp_name emp.ename%TYPE :=UPPER(‘&employee_name’);
v_count NUMBER(3):=0;
CURSOR emp_salary_cur IS
SELECT empno,ename,hiredate,job,sal, TO_CHAR(sysdate,’yyyy’) –
TO_CHAR(hiredate,’yyyy’) experience
FROM emp
WHERE hiredate > ALL (SELECT hiredate
FROM emp
WHERE ename = v_emp_name)
FOR UPDATE OF sal NOWAIT;
staff_rec emp_hiredate_cur%ROWTYPE;
BEGIN
OPEN emp_salary_cur;
LOOP
FETCH emp_salary_cur INTO staff_rec;
EXIT WHEN emp_salary_cur%NOTFOUND;
DSO23BT/SFW20BT @22020/21 30
d) Cursors with Subqueries.
A subquery is a query that appears within another query and is enclosed by parentheses. The
subquery provides a value or set of values to the outer query.
The expression below is a cursor that uses a subquery to calculate the to number of
employees per department and list all departments with more than 3 employees.
DECLARE
CURSOR tot_employees_cur IS
SELECT d.dname,d.deptno,e.total_staff
FROM dept d,(SELECT deptno, COUNT(*) total_staff
FROM emp e
GROUP BY deptno) e
WHERE d.deptno=e.deptno
AND e.total_staff >3;
BEGIN
DBMS_OUTPUT.PUT_LINE (RPAD('Department Name',15)||' No. of Employees');
DBMS_OUTPUT.PUT_LINE (RPAD('-------------------',15)||' ---------------------');
FOR dept_rec IN tot_employees_cur LOOP
DBMS_OUTPUT.PUT_LINE(RPAD(dept_rec.dname,15) || dept_rec.total_staff);
END LOOP;
END;
/
DSO23BT/SFW20BT @22020/21 31
CHAPTER 8
Handling Exceptions
− An Oracle error occurs, and the associated exception is raised automatically. For
example, if the error ORA-01403 occurs when no rows are retrieved from the database
in a SELECT statement, then PL/SQL raise the exception NO_DATA_FOUND.
− You raise an exception explicitly by issuing the RAISE statement within the block.
Propagating an Exception
The moment the exception is raised in the executable section of the block and there is no
corresponding exception handler, the PL/SQL block terminates with the failure and the
exception is propagated to the calling environment.
Example:
DECLARE
v_emp_id employees.employee_id%TYPE:=&employee_no;
v_fname employees.first_name%TYPE;
v_lname employees.last_name%TYPe;
v_job employees.job_id%TYPE;
v_salary employees.salary%TYPE;
BEGIN
SELECT first_name,last_name,job_id,salary
INTO v_fname,v_lname,v_job,v_salary
FROM employees
WHERE employee_id=v_empid;
DBMS_OUTPUT.PUT_LINE(UPPER(v_fname||' '||v_lname)||' is employed as '||v_job||'
and earns '||TO_CHAR(v_salary,'l999,999.99'));
END;
/
DSO23BT/SFW20BT @22020/21 32
The moment it’s executed the following happens;
Enter value for employee_no: 300
DECLARE
*
ERROR at line 1:
ORA-01403: no data found
ORA-06512: at line 8
Due to the fact that exception handler is not used the PL/SQL display this message to display
the error the block has encountered when it was executed.
Trapping an Exception
The moment the exception is raised in the executable section of the block, processing
branches to the to the corresponding exception handler in the exception section of the
block. If PL/SQL handles the exception, then the exception does not propagate to the
enclosed block.
PL/SQL has three types of Exceptions , of which two are implicitly raised and the other one is
explicitly raised.
DSO23BT/SFW20BT @22020/21 33
a) Predefined Exception
It has predefined names that can be used to specific error trapping, no declaration is
needed.
DECLARE
v_emp_id employees.employee_id%TYPE;
v_fname employees.first_name%TYPE;
v_lname employees.last_name%TYPe;
v_job employees.job_id%TYPE:=UPPER('&job_title');
v_salary employees.salary%TYPE;
BEGIN
SELECT employee_id,first_name,last_name,job_id,salary
INTO v_emp_id,v_fname,v_lname,v_job,v_salary
FROM employees
WHERE job_id=v_job;
DBMS_OUTPUT.PUT_LINE(UPPER(v_fname||' '||v_lname)||'('||v_emp_id||') is
employed as '||v_job||' and earns '||TO_CHAR(v_salary,'fml999,999.99'));
EXCEPTION
WHEN NO_DATA_FOUND THEN --if no row is found by the query then this error will be raised
DBMS_OUTPUT.PUT_LINE(v_job||' does not exists.');
WHEN TOO_MANY_ROWS THEN --if more than one row is found by the query then this error
will be raised
DBMS_OUTPUT.PUT_LINE(v_job||' has more than one employee.');
WHEN OTHERS THEN --this only become raised if the other errors are met
DBMS_OUTPUT.PUT_LINE('No other errors');
END;
/
DSO23BT/SFW20BT @22020/21 34
b) Nonpredefined Exception
It is like predefined exception but do not have predefined names but uses Oracle
error number (ORA,’####’) and error message. It uses EXCEPTION_INIT function. You
need to declare.
DECLARE
v_emp_id employees.employee_id%TYPE;
v_fname employees.first_name%TYPE;
v_lname employees.last_name%TYPE;
v_job employees.job_id%TYPE:=UPPER('&job_title');
v_salary employees.salary%TYPE;
e_more_records EXCEPTION;
PRAGMA EXCEPTION_INIT(e_more_records,-01422);
BEGIN
SELECT employee_id,first_name,last_name,job_id,salary
INTO v_emp_id,v_fname,v_lname,v_job,v_salary
FROM employees
WHERE job_id=v_job;
DBMS_OUTPUT.PUT_LINE(UPPER(v_fname||' '||v_lname)||'('||v_emp_id||') is
employed as '||v_job||' and earns '||TO_CHAR(v_salary,'fml999,999.99'));
EXCEPTION
WHEN e_no_records THEN --if no row is found by the query then this error will be
raised
DBMS_OUTPUT.PUT_LINE(v_job||' does not exists.');
WHEN e_more_records THEN --if more than one row is found by the query then this
error will be raised
DBMS_OUTPUT.PUT_LINE(v_job||' has more than one employee.');
END;
/
DSO23BT/SFW20BT @22020/21 35
c) User-defined Exception
DECLARE
v_emp_id employees.employee_id%TYPE;
v_lname employees.last_name%TYPE;
v_job employees.job_id%TYPE:=UPPER('&job_title');
v_salary employees.salary%TYPE;
v_count NUMBER(2):=0;
e_no_records EXCEPTION; --Declare the non-predefine exception her
e_more_records EXCEPTION;
BEGIN
SELECT COUNT(*) count –to count the number of rows retrieved.
INTO v_count
FROM employees
WHERE job_id=v_job;
IF v_count = 0 THE
RAISE e_no_records;
ELSIF v_count > 1 THEN
RAISE e_more_records;
ELSE
SELECT employee_id, last_name,job_id,salary
INTO v_emp_id,v_fname, v_job,v_salary
FROM employees
WHERE job_id=v_job;
DBMS_OUTPUT.PUT_LINE(UPPER(v_lname)||'('||v_emp_id||')
is employed as '||v_job||' and earns '||TO_CHAR(v_salary,'fml999,999.99'));
END IF;
EXCEPTION
WHEN e_no_records THEN--if no row is found by the query then this error will be
raised
DBMS_OUTPUT.PUT_LINE(v_job||' does not exists.');
WHEN e_more_records THEN --if more than one record is found by the query then this
error will be raised
DBMS_OUTPUT.PUT_LINE(v_job||' has more than one employee.');
END;
/
DSO23BT/SFW20BT @22020/21 36
CHAPTER 9
Creating Procedures
a) Subprogram:
• Is a named PL/SQL block that can accept parameters and be invoked from a calling
environment.
• They are of two types:
− A procedure that performs an action.
− A function that computes a value and return answer.
• Is based on standard PL/SQL block structure.
• Provides modularity, reusability, extensibility, and maintainability.
• Provides easy maintenance, improved data security and integrity, improved
performance, and improved code clarity.
• Are named PL/SQL blocks that can accept parameters and be invoked from a
calling environment.
• Subprogram specification:
− The header is relevant for named blocks only and determines the way that
the program unit is called or invoke.
The header determines:
The PL/SQL subprogram type, i.e., either a procedure or function.
The name of the subprogram.
The parameter list if one exists.
The RETURN clause, which applies only to functions.
− The IS or AS keyword is mandatory.
• Subprogram body:
− The declaration section of the block is between IS|AS and BEGIN. The
DECLARE word used with anonymous block is not used here.
− The executable section between BEGIN and END is compulsory, encloses the
body of actions to be performed.
− The exception section between EXCEPTION and END is optional.
DSO23BT/SFW20BT @22020/21 37
b) Procedures
it is a named PL/SQL block that can accept parameters and be invoked. Procedures are
used to perform actions. It has a header, a declaration section, an executable section,
and an optional exception-handling section.
END procedure_name;
DSO23BT/SFW20BT @22020/21 38
A procedure without errors shows this message: Procedure created .
DSO23BT/SFW20BT @22020/21 39
The IN and OUT parameters.
The example below shows how a procedure with few IN parameters and few OUT
parameters works. The output can be displayed by either using an anonymous block or
binding variables block that must call the procedure.
DECLARE
v_emp_no employees.first_name%TYPE:=&employeesno;
v_firstname employees.first_name%TYPE;
v_lastname employees.last_name%TYPE;
v_hiredate employees.hire_date%TYPE;
v_job_title employees.job_id%TYPE;
v_salary employees.salary%TYPE;
BEGIN
emp_infor(&v_emp_no,v_firstname,v_lastname,v_hiredate,v_job_title,v_salary);
--procedure being called with corresponding parameters
DBMS_OUTPUT.PUT_LINE(UPPER(v_firstname||' '||v_lastname)||' employed as '||
UPPER(v_job_title)||' from the '||TO_CHAR(v_hiredate,'ddth "of" fmMONTH yyyy')||
' with the salary of '||TO_CHAR(v_salary,'fmL999,999.99'));
END;
/
DSO23BT/SFW20BT @22020/21 40
Enter value for employee_no: 117
SIGAL TOBIAS employed as PU_CLERK from the 24th of JULY 1997 with the salary of
R2,800.
EXECUTE mp_infor(117,:g_firstname,:g_lastname,:g_hiredate,:g_job_title,:g_salary);
PRINT g_firstname
PRINT g_lastname
PRINT g_hiredate
PRINT g_job_title
PRINT g_salary
DSO23BT/SFW20BT @22020/21 41
Procedure with a cursor
emp_rec emp_list_cur%ROWTYPE;
BEGIN
OPEN emp_list_cur;
DBMS_OUTPUT.PUT_LINE('Employee Name'||CHR(9)||'Hiredate'||CHR(9)
||'Job Title'||CHR(9)||'Salary');
DBMS_OUTPUT.PUT_LINE('-------------'||CHR(9)||'---------------'||CHR(9)
||'----------------'|| CHR(9)||'---------------');
LOOP
FETCH emp_list_cur INTO emp_rec;
EXIT WHEN emp_list_cur%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(INITCAP(emp_rec.first_name)||', '||
INITCAP(emp_rec.last_name)||CHR(9)||TO_CHAR(emp_rec.hire_date,'dd Month
yyyy')||CHR(9)||INITCAP(emp_rec.job_id)||CHR(9)||
TO_CHAR(emp_rec.salary,'l999,999.99'));
END LOOP;
CLOSE emp_list_cur;
END emp_infor;
/
DECLARE
v_job_title employees.job_id%TYPE:=UPPER('&job_title');
BEGIN
emp_infor(v_job_title); -- procedure being called or being invoked.
END;
/
DSO23BT/SFW20BT @22020/21 42
Enter value for job_title: sh_sales
Employee Name Hiredate Job Title Salary
------------------------- ------------------------- ---------------- -----------------------
Method Description
Positional Specify the same parameters in the same order as they are declared
in the procedure. e.g., emp_infor(empno,job)
Named association Specify the name of each parameter along with its value. The arrow
=> serves as the association operator.
e.g., emp_infor(empno => v_emplono,job => v_job_title)
Combination Specify the first parameter with positional notation, the switch to the
named association for the last parameter.
e.g., emp_infor(empno, jpb =>v_job_title)
Removing Procedures
To remove a procedure just like any other object we code DROP PROCEDURE procedure_name.
Benefits of Subprograms:
• Easy maintenance.
• Improved data security and integrity
• Improved performance
• Improved code.
DSO23BT/SFW20BT @22020/21 43
CHAPTER 10
Creating Functions
b) Creating a function
As mentioned above, a function is a PL/SQL block that returns a value. You create new
functions with the CREATE FUNCTION statement which may declare a list of parameters, must
return one value, and must define the actions to be performed. The REPLACE statement as it
is also used in most SQL and PL/SQL objects indicates that if an object such as a function
exists, it will be updated with the new code added to it.
END function_name;
Run the block to store the source code and compile the function.
If it returns errors the use SHOW ERRORS to see these compilation errors and rectify them.
DSO23BT/SFW20BT @22020/21 44
E.g., the expression below creates a function named total_staff that accept job_id as an input
parameter and calculated the total number of employees employed within the job_id.
RETURN v_tot_staff;
END total_staff;
/
c) Executing Functions
A function as created above may accept one or many parameters but can return a single
value. Just like in the PROCEDURE, the FUNCTION parameter may only use IN modes,
because the purpose of the function is to accept no or more actual parameters and return a
single value.
There are few ways that can be used to invoke a function as part of a PL/SQL expression.
The calling code must have a declared variable to hold the returned value.
Execute the function. The variable will be populated by the value returned through a RETURN
statement.
PRINT g_total_emps
G_TOTAL_EMPS
----------------------
20
DSO23BT/SFW20BT @22020/21 45
Invoke the function using an anonymous block.
DECLARE
v_job_title employees.job_id%TYPE:=UPPER('&job_title');
v_total_emps NUMBER(3):=0;
BEGIN
v_total_emps:=total_staff(v_job_title); -- calling a function by a block
E.g., the expression creates a procedure names total_employees that accept a job_title as a
parameter and pass it to a cursor named job_title_total_cur to retrieve firstname, lastname,
salary of each employee under the job_title accepted by the procedure and store them in a
record named staff_list_rec that have the structure of the cursor.
Create an anonymous block that call the procedure and display a list of the employees
retrieved by the cursor, calculate the total_salary of that job_title.
Call the function to display the number of the employees the function has returned.
Display the total_employess and total_salary per job_title.
.
DSO23BT/SFW20BT @22020/21 46
CREATE OR REPLACE PROCEDURE total_employees(p_job_title IN employees.job_id%TYPE)
IS
CURSOR job_title_total_cur IS
SELECT first_name,last_name,salary
FROM employees
WHERE job_id = p_job_title; --passing parameter to the cursor
DECLARE
v_total_staff NUMBER(3);
v_total_salary NUMBER(12,2):=0;
v_job_title employees.job_id%TYPE:=UPPER('&job_id');
BEGIN
DBMS_OUTPUT.PUT_LINE('A list and totals of the job title: '||v_job_title);
DBMS_OUTPUT.PUT_LINE('--------------------------------------------------------');
DSO23BT/SFW20BT @22020/21 47
Enter value for job_id: it_prog
A list and totals of the job title: IT_PROG
--------------------------------------------------------
Employee Name Salary
----------------------- -----------------------
Alexander, Hunold R9,000.00
Bruce, Ernst R6,000.00
David, Austin R4,800.00
Valli, Pataballa R4,800.00
Diana, Lorentz R4,200.00
DSO23BT/SFW20BT @22020/21 48