Vous êtes sur la page 1sur 12

Cursor Examples

CURSOR WITHOUT PARAMETERS


The result set of this cursor is all course_numbers whose course_name matches the
variable called name_in.
CREATE OR REPLACE Function FindCourse
( name_in IN varchar2 )
RETURN number
IS
cnumber number;

CURSOR c1
IS
SELECT course_number
FROM courses_tbl
WHERE course_name = name_in;

BEGIN

OPEN c1;
FETCH c1 INTO cnumber;

if c1%notfound then
cnumber := 9999;
end if;

CLOSE c1;

RETURN cnumber;

END;

Ex2:
DECLARE
v_Cust_noCustomers.Cust_no%TYPE;
CURSORc_customerIS
SELECTCust_no
FROMCustomers
WHERECust_no<710;
BEGIN
OPENc_customer;
LOOP
FETCHc_customerINTOv_Cust_no;
EXITWHENc_customer%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('CustomerID:'||v_Cust_no);
ENDLOOP;
CLOSEc_customer;
EXCEPTION
WHENOTHERS
THEN
IFc_customer%ISOPEN
THEN
CLOSEc_customer;
ENDIF;
END;

Ex3:
DECLARE
CURSOR c_emp_detail IS
SELECT employee_id,first_name,last_name,salary
FROM employees;
rec_emp_detail c_emp_detail%ROWTYPE;
/* A cursor based record is based on elements of pre-Defined cursor.
A cursor based record can be only declared after its corresponding
cursor, else an error occurs.*/
BEGIN
OPEN c_emp_detail;
LOOP
FETCH c_emp_detail INTO rec_emp_detail;
EXIT WHEN c_emp_detail%NOTFOUND; -- cursor attribute to exit when no rows
found to fetch.
DBMS_OUTPUT.PUT_LINE('Employees Details : '||' '||
rec_emp_detail.employee_id
||' '||rec_emp_detail.first_name||' '||rec_emp_detail.last_name);
END LOOP;
DBMS_OUTPUT.PUT_LINE('Total number of rows : '||c_emp_detail%ROWCOUNT);
-- cursor attribute to find the total number of rows executed.
CLOSE c_emp_detail;
END;

Ex4:
DECLARE
CURSOR c_emp_detail IS
SELECT employee_id,first_name,last_name,salary
FROM employees;
/*declaring a record datatype, with same datatype of tables of database using %TYPE attribute,
with same order of corresponding cursor */
TYPE type_rectype IS RECORD
(emp_id employees.employee_id%TYPE,
f_name employees.first_name%TYPE,
l_name employees.last_name%TYPE,
s_salary employees.salary%TYPE
);
rec_type type_rectype; --variable of record datatype.
BEGIN
OPEN c_emp_detail;
LOOP
FETCH c_emp_detail INTO rec_type; -- Fetches the cursor into record variable.
EXIT WHEN c_emp_detail%NOTFOUND;
-- variable is part of each record datatype,so to reference it use dot notation in DBMS_OUTPUT.
DBMS_OUTPUT.PUT_LINE('Employees Details : '||' '||rec_type.emp_id
||' '||rec_type.f_name||' '||rec_type.l_name||' '||rec_type.s_salary);
END LOOP;
DBMS_OUTPUT.PUT_LINE('Total number of Employees : '||c_emp_detail%ROWCOUNT);
CLOSE c_emp_detail;
END;

Example 4. Cursors in Nested Loops


Hide Shrink

DECLARE
CURSOR c_dept IS
SELECT *
FROM departments
WHERE manager_id IS NOT NULL
ORDER BY department_name;
r_dept c_dept%ROWTYPE;
-- Declaration of department cursor and record variable.
CURSOR c_emp (c_dept_no departments.department_id%TYPE) IS
SELECT *
FROM employees
WHERE department_id = c_dept_no;
r_emp c_emp%ROWTYPE;
-- Declaration of employees cursor and record variable.
BEGIN
OPEN c_dept;

Copy Code

LOOP
FETCH c_dept INTO r_dept;
EXIT WHEN c_dept%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('----------------------------------');
DBMS_OUTPUT.PUT_LINE('Department Name : '||r_dept.department_name);
DBMS_OUTPUT.PUT_LINE('----------------------------------');
OPEN c_emp(r_dept.department_id);
LOOP
FETCH c_emp INTO r_emp;
EXIT WHEN c_emp%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('Employees Details : '||r_emp.employee_id
||' '||r_emp.first_name||' '||r_emp.last_name||' '||r_emp.salary);
END LOOP;
CLOSE c_emp;
END LOOP;
CLOSE c_dept;
END;

Cursor For Loops


In Cursor for loops, the process of opening, fetching and closing is handled implicitly.
This makes the programmer code and maintain the blocks easily.
In cursor for loop, before each iteration PL/SQL fetches into implicitly declared record.
The sequence of statements inside the loop is executed once for each row that satisfies
the query.
When loop is left, the cursor is automatically closed.
The cursor is closed even if you use an EXIT or GOTO statement to leave the loop
before all rows are fetched.

Example 5. Implicit cursor for loop


Hide Copy Code

BEGIN
FOR item IN(SELECT department_name,d.department_id,last_name,job_id,salary
FROM departments d JOIN employees e
ON e.department_id = d.department_id
WHERE JOB_ID
= 'IT_PROG'
AND salary
> 4800)
LOOP
DBMS_OUTPUT.PUT_LINE(item.last_name||' '||item.department_name
||' '||item.department_id||' '||item.job_id||' '||item.salary);
END LOOP;

END;

If you need the same query to reference from different parts of the same procedure, you
can declare a cursor with that specific query,and get the results using cursor for loop.
For this, I am going to use the same selectquery from Example 5.

Example 6. Explicit cursor for loop


Hide Copy Code

DECLARE
CURSOR c_detail IS
SELECT department_name,d.department_id,last_name,job_id,salary
FROM departments d JOIN employees e
ON e.department_id = d.department_id
WHERE JOB_ID
= 'IT_PROG'
AND salary
> 4800;
BEGIN
FOR item IN c_detail
LOOP
DBMS_OUTPUT.PUT_LINE(item.last_name||' '||item.department_name
||' '||item.department_id||' '||item.job_id||' '||item.salary);
END LOOP;
END;
Hide Copy Code

Hunold IT 60 IT_PROG 9000


Ernst IT 60 IT_PROG 6000

xample 7. Nested Cursors Using Cursor FOR Loops


If you notice, example 4 and example 7 outputs are the same, the difference is example
7. We are using cursor for loop where record variable declares r_dept and r_emp ,
opening fetching and closing is done automatically by each loop iteration until all rows
are fetched according to the specific query in cursor.
The variable v_dept_id is initialized to be the department_id of the current record of
the c_dept cursor. Thec_dept cursor ties in the c_emp cursor by means of this variable.
Thus, when the cursor c_emp is processed, it is retrieving employees who have
the department_id of the current record for the c_dept cursor.
Each iteration of the c_dept cursor will execute the DBMS_OUTPUT only once.
The DBMS_OUTPUT will be executed once for each iteration of the c_emp cursor loop,
producing a line of output for each employee.
Hide Copy Code

DECLARE
v_dept_id departments.department_id%TYPE;
CURSOR c_dept IS
SELECT *
FROM departments
WHERE manager_id IS NOT NULL
ORDER BY department_name;
CURSOR c_emp IS
SELECT *

FROM employees
WHERE department_id = v_dept_id;
BEGIN
FOR r_dept IN c_dept
LOOP
v_dept_id := r_dept.department_id;
DBMS_OUTPUT.PUT_LINE('----------------------------------');
DBMS_OUTPUT.PUT_LINE('Department Name : '||r_dept.department_name);
DBMS_OUTPUT.PUT_LINE('----------------------------------');
FOR r_emp IN c_emp
LOOP
DBMS_OUTPUT.PUT_LINE('Employee Name : '||r_emp.last_name);
END LOOP;
END LOOP;
END;
Hide Copy Code

---------------------------------Department Name : Accounting


---------------------------------Employee Name : Higgins
Employee Name : Gietz
-------------------------------------------------------------------------------------Department Name : Shipping
---------------------------------Employee Name : OConnell
Employee Name : Grant
--------------------

Example 8. Using Parameters with Nested Cursors For Loops


Hide Shrink

CREATE OR REPLACE PROCEDURE print_emp_dept(v_lo_id IN locations.location_id%TYPE)


IS
v_flag departments.department_id%TYPE;
CURSOR c_locations IS
SELECT *
FROM locations
WHERE location_id = v_lo_id;
CURSOR c_departments(v_loc_id locations.location_id%TYPE) IS
SELECT l.location_id,department_name,department_id
FROM locations l JOIN departments d
ON l.location_id = d.location_id
WHERE l.location_id = v_loc_id
AND d.manager_id IS NOT NULL;
CURSOR c_employees (v_dept_id departments.department_id%TYPE,
v_loc_id locations.location_id%TYPE) IS
SELECT d.department_id,employee_id,first_name,last_name,salary,job_id,city
FROM locations l JOIN departments d

Copy Code

ON l.location_id = d.location_id
JOIN employees e
ON d.department_id = e.department_id
WHERE d.department_id = v_dept_id
AND l.location_id = v_loc_id;
BEGIN
FOR r_location IN c_locations
LOOP
DBMS_OUTPUT.PUT_LINE
('Location ID : '||r_location.location_id||
' Belong''s to '||r_location.city||' city');
DBMS_OUTPUT.PUT_LINE('In city '||r_location.city||' '||'Departments are ');
FOR r_department IN c_departments(r_location.location_id)
LOOP
DBMS_OUTPUT.PUT_LINE('-----------------------------------------');
DBMS_OUTPUT.PUT_LINE('Department ID :
'||r_department.department_id||' '||'Location ID : '||
r_department.location_id||' '||
'Department Name : '||r_department.department_name);
DBMS_OUTPUT.PUT_LINE('-----------------------------------------');
v_flag := r_department.department_id;
EXIT WHEN v_flag IS NULL;
FOR r_employee IN c_employees
(r_department.department_id,r_location.location_id)
LOOP
DBMS_OUTPUT.PUT_LINE(r_employee.employee_id||'
'||r_employee.first_name||' '||
r_employee.last_name||' work''s in city '||r_employee.city);
END LOOP;
END LOOP;
END LOOP;
IF v_flag IS NULL THEN
DBMS_OUTPUT.PUT_LINE('Not Available');
END IF;
END print_emp_dept;

Call the procedure:


Hide Copy Code

CALL print_emp_dept(3000);
BEGIN
print_emp_dept(1700);
END;
Hide Copy Code

Location ID : 1400 Belong's to Southlake city


In city Southlake Departments are
----------------------------------------Department ID :60 Location ID : 1400 Department Name : IT
----------------------------------------103 Alexander Hunold work's in city Southlake
104 Bruce Ernst work's in city Southlake
105 David Austin work's in city Southlake
106 Valli Pataballa work's in city Southlake

107 Diana Lorentz work's in city Southlake

The grandparent cursor, c_locations is declared. It is a collection of locations and takes


parameter while call of procedure print_emp_dept with a location_id is available in
database.
The parent cursor, c_departments is declared. It takes in the parameter
of location_ID to generate a list of departments that belongs to parameter location_ID.
The child cursor, c_employees takes in two
parameters, department_id and location_id. In this way, it generates a list of
employees working with different departments but in the same location_id.
The grandparent cursor loop begins, and only the city name and location_id is displayed
with DBMS_OUTPUT. The parent cursor loop begins. It takes the parameter
of location_id from the grandparent cursor
wheredepartment_id, location_id and department_name is displayed.
The child cursor loop begins. It takes in the parameter of department_id from the
parent cursor andlocation_id from the grandparent cursor.The employee_id and
employees name is displayed in which city they work because of the parameter which
has been used from grandparent cursor.
In this complete process, the child cursor loop ends first, then parent cursor loop ends,
then grandparent cursor loop ends.

Example 9. For UPDATE and WHERE CURRENT CLAUSE


The FOR UPDATE clause is only used in cursor, when update or delete statements are
used for database tables. Normally, when programmer executes SELECT statement,
there is no locking of rows. The main aim to use FOR UPDATE clause is to lock rows
when performing update or delete statements inside the cursor, and restrict other
users to perform any updation in particular database tables. Once the updation is done
inside the cursor, then COMMIT or ROLLBACK placed inside the execution block
releases the lock. Now FOR UPDATEclause with particular Column name i.e.. FOR
UPDATE salary will only lock salary column according to selectstatement even if there
is join condition, else FOR UPDATE clause will lock entire row of that particular table.
NOWAIT is an optional keyword, if the rows are already locked by another programmer,
then control is immediately returned to programmer so that meanwhile he can do other
work before trying again. If you omit the keyword, then the wait may be a long period of
time.
The WHERE CURRENT OF clause can be used only if FOR UPDATE clause is used in
cursor.
The WHERE current of clause only references the cursor which fetches the latest row.
The WHERE CURRENT OF CLAUSE is useful to eliminate the where condition
in update clause.
First, I have created a demo table, because I don't want to change data in the actual
table of database.
Hide Copy Code

create table emp1 as select * from employees;


create table dept1 as select * from departments;
Hide Copy Code

DECLARE
CURSOR c_sal_update IS
SELECT employee_id,first_name,last_name,job_id,department_name,e.department_id,salary
FROM dept1 d , emp1 e
WHERE e.department_id = 80
FOR UPDATE OF salary NOWAIT;
rec_sal c_sal_update%ROWTYPE;
BEGIN
OPEN c_sal_update; -- rows are locked.
LOOP
FETCH c_sal_update INTO rec_sal;
EXIT WHEN c_sal_update%NOTFOUND;
IF rec_sal.job_id = 'SA_MAN' THEN
UPDATE emp1
SET salary = rec_sal.salary + 1000
WHERE CURRENT OF c_sal_update;
END IF;
END LOOP;
COMMIT; -- rows are unlocked.
CLOSE c_sal_update;
END;

OUTPUT
Hide Copy Code

SELECT employee_id,first_name,last_name,job_id,department_id,salary
FROM emp1
WHERE job_id = 'SA_MAN'
AND department_id = 80;
EMPLOYEE_ID FIRST_NAME
LAST_NAME
JOB_ID
DEPARTMENT_ID SALARY
----------- --------------- ----------------- ---------- ------------- -------145 John
Russell
SA_MAN
80 15000
146 Karen
Partners
SA_MAN
80 14500
147 Alberto
Errazuriz
SA_MAN
80 13000
148 Gerald
Cambrault
SA_MAN
80 12000
149 Eleni
Zlotkey
SA_MAN
80 11500

Example 10. Cursor attributes with explicit cursor


Hide Shrink

DECLARE
CURSOR c_high_sal IS
SELECT *
FROM (SELECT employee_id,first_name,last_name,salary
FROM employees ORDER BY salary DESC)
WHERE ROWNUM < 11;
high_sal c_high_sal%ROWTYPE;

Copy Code

BEGIN
IF NOT c_high_sal%ISOPEN THEN
DBMS_OUTPUT.PUT_LINE('Cursor is Closed');
END IF;
OPEN c_high_sal;
IF c_high_sal%ISOPEN THEN
DBMS_OUTPUT.PUT_LINE('Cursor is open');
END IF;
LOOP
FETCH c_high_sal INTO high_sal;
IF c_high_sal%FOUND THEN
DBMS_OUTPUT.PUT_LINE(high_sal.employee_id||' '||high_sal.first_name
||' '||high_sal.last_name||' '||high_sal.salary);
ELSE
EXIT; -- the same as exit when c_high_sal%NOTFOUND;
END IF;
END LOOP;
DBMS_OUTPUT.PUT_LINE(' Number of rows fetched : '||c_high_sal%ROWCOUNT);
CLOSE c_high_sal;
IF NOT c_high_sal%ISOPEN THEN
DBMS_OUTPUT.PUT_LINE('Cursor is closed ');
END IF;
END;
Hide Copy Code

Cursor is Closed
Cursor is open
100 Steven King 24000
101 Neena Kochhar 17000
102 Lex De Haan 17000
145 John Russell 14000
146 Karen Partners 13500
201 Michael Hartstein 13000
205 Shelley Higgins 12000
108 Nancy Greenberg 12000
147 Alberto Errazuriz 12000
168 Lisa Ozer 11500
Number of rows fetched : 10
Cursor is closed

The cursor declared in the preceding example executes highest salary paid employees.
Using sub-query in cursor. declare high_sal with %ROWTYPE.
The cursor is not yet open, %ISOPEN attribute is used to see whether cursor is open
with help of IF-THENstatement, where %ISOPEN turn TRUE.
Cursor is open, now to show with DBMS_OUTPUT, %ISOPEN is used with IFTHEN statement, where %ISOPEN turnTRUE.
Now loop start and cursor c_high_sal is fetched into variable high_sal,
again %FOUND attribute is used withIF-THEN-ELSE statement to show the list of
highest paid employees, where %FOUND attribute turns true till the last fetch of cursor
and exits from the loop.

Once it is out of the loop, DBMS_OUTPUT fetches the last number of row of loop and
shows the figure with%ROWCOUNT attribute. Cursor is closed.
After the cursor is closed, %ISOPEN is used with the help of IF-THEN statement to show
the cursor is closed.

Example 11. Cursor attributes with Implicit cursor


Hide Copy Code

DECLARE
v_dept departments.department_id%TYPE := 270;
v_dept_name departments.department_name%TYPE;
BEGIN
DELETE FROM dept1 WHERE department_id = v_dept;
IF SQL%FOUND THEN
INSERT INTO dept1 VALUES(270,'Personnel',200,1700);
END IF;
DBMS_OUTPUT.PUT_LINE('Number of rows inserted : '||SQL%ROWCOUNT);
SELECT department_name INTO v_dept_name
FROM dept1
WHERE department_id = 270;
DBMS_OUTPUT.PUT_LINE('Department Name : '||v_dept_name);
END;
Hide Copy Code

Number of rows inserted : 1


Department Name : Personnel

An implicit cursor can tell you how many rows were affected by
an update, insert and delete with the help of %FOUND and %ROWCOUNT attribute.
We have already created a table named dept1 in the previous example. In the following
example, variablesv_dept and v_dept_name are reference by table departments, as we
are working on table dept1,it will not give any error unless the structure or datatype is
different from the other table, i.e., departments and dept1.
Deleting from dept1 table where department_id is 270.
Using SQL instead of cursor name as it is implicit cursor with no cursor name.
Use %FOUND attribute with IF-THEN statement to insert row in dept1 table with
new department_name, i.e.,Personnel. Once it is inserted to know how many rows
were affected, use %ROWCOUNT in DBMS_OUTPUT.
In same execution block using v_dept_name, we are fetching new department name
with select statement.
I have not placed any commit because I don't want to update any row so later I will
rollback it.
To learn more about implicit cursor and its attributes, refer to PL/SQL User's Guide and
Reference chapter 6.

NOTE: Between implicit and explicit cursor, the fastest is implicit cursor, because
opening and closing of cursor and looping is automatically handled by Oracle. Cursor for
loop is also an implicit cursor where opening and closing of cursor in done implicitly. But
cursor should be used according to requirement because each type of cursor has its
own advantages and disadvantages. To know more about this, you can refer to Steven
Feuerstein's book PL-SQL Programming 5th edition.

Vous aimerez peut-être aussi