CREATE PROCEDURE fire_employee (emp_id NUMBER) AS
BEGIN
DELETE FROM emp WHERE eid = emp_id;
END;
execute fire_employee(123)
create procedure getStudent(sid number) as
sss number;
begin
select age into sss from student where snum=sid;
end;
What is Cursor?
A cursor is a variable that runs through the tuples of some relation. This relation can be a stored table, or it can be the answer to some query.
Why do we need the Cursors?
• SELECT statement should return only one row at a time in previous PL/SQL programs. This is too restrictive in many applications. For Example:
DECLARE
Student_record student%ROWTYPE;
BEGIN
SELECT * INTO student_record
FROM student;
SELECT department INTO v_department
FROM classes
WHERE room_id = 999;
END;
The above example works only if one row is selected.
•
We
use the idea of Cursor to handle the above problem. Cursor is the mechanism that
allow the program to step through the rows one at a time.
• By fetching into the cursor each tuple of the relation, we can write a program
to read and process the value of each such tuple. If the relation is stored, we
can also update or delete the tuple at the current cursor position.
Different types of Cursors
• Explicit
Cursor and implicit Cursor (e.g., SQL statement in PL/SQL)
•
Static cursor and Dynamic cursor
•
Client side cursor and Server side cursor
Explicit cursor (the cursor name is explicitly
assigned to a SELECT statement) involves four steps
(a)
Declare the cursor (static part)
=> CURSOR
cursor_name IS select_statement;
(b) Open
the cursor for a query
=> OPEN
cursor_name;
(c) Fetch the results into PL/SQL
variables
=> FETCH
cursor_name INTO variable_list;
(d) Close
the cursor
=> CLOSE
cursor_name;
• The cursor declaration is the only step that goes in the PL/SQL declarative section.
Implicit cursor is used for all other SQL statements (INSERT, UPDATE, DELETE, and single-row SELECT…INTO)
• Actually PL/SQL engine takes care of the above four steps automatically
Drawbacks of Implicit Cursors
• It is less efficient than
an explicit cursor -- it performs minimum two fetches to see if it has
more
than one rows.
• It is more vulnerable to data errors.
• It gives you less
programmatic control.
Even if your query returns only a single row, you might still decide to use
an explicit cursor.
However, there is no explicit cursor for
UPDATE, DELETE, and INSERT statements.
Declaring a Cursor in the DECLARE section
employee_rec employee%ROWTYPE;
CURSOR
employee_cur IS --
employee_cur is not
declared
SELECT
* FROM employee;
--
this is a static cursor
• SELECT statement contains no INTO clause.
• A cursor
declaration can reference PL/SQL variables in the WHERE clause.
The above cursor declaration associates the entire employee table with the cursor named employee_cur.
The SQL is static (or static SQL), which means that the content of the SQL statement is fixed (static).
The name of an explicit cursor is not a PL/SQL variable. Instead, it is an undeclared identifier used to refer to the query. You can't assign values to a cursor, nor can you use it in an expression.
PL/SQL variables in a Cursor
In native SQL, the SELECT list may contain both columns and expressions. In PL/SQL, the SELECT list may contain PL/SQL variables, expressions, and even functions as well as host language bind variables (> PL/SQL 2.1).
DECLARE
project_bonus
NUMBER := 1000;
CURSOR
employee_cur
IS
SELECT
employee_id, salary + project_bonus
New_salary
FROM
employee
WHERE
hiredate < ADD_MONTHS (SYSDATE, -36);
BEGIN
…
END;
Note that it requires always a name for each column entry in the SELECT statement for a cursor. For example in a cursor declaration, the following SQL statement is not allowed:
SELECT state, SUM(salary)
FROM employee
GROUP BY state;
In this SQL, the SUM(salary) needs a name like
SELECT state, SUM(salary) AS sum_salary
FROM employee
GROUP BY
state;
Opening a Cursor
e.g., in the BEGIN
section
OPEN
employee_cur;
When a cursor is opened, the following things happen:
• The values of the bind variables are examined.
• Based on the values of
the bind variables, the active set (the query result) is
determined.
• The active set pointer is set to the first row.
Fetching from a Cursor
Syntax: FETCH cursor_name INTO
record_or_variable_list;
e.g.,
FETCH employee_cur INTO
employee_rec;
e.g.,.
FETCH employee_cur INTO name, age, salary
• When you fetch into a list of variables or record, the number of
variables and types in the record must match the
number of expressions in the SELECT list of the cursor.
•
After each FETCH, the active set pointer is increased to the
next row.
• You can FETCH from it until there are no more records left in the
active set. At this point the %NOTFOUND cursor
attribute for the cursor is set to TRUE. So, %NOTFOUND attribute is
used to determine when the entire active set has been retrieved.
•
The last fetch will assign NULL to the output
variables.
Closing a Cursor
e.g.,
CLOSE employee_cur;
e.g.,
IF employee_cur%ISOPEN
THEN
CLOSE employee_cur;
END
IF
The resources associated with it (memory space) can be freed.
When execution of the block terminates, PL/SQL will automatically close any local cursors. But don't depend on the runtime engine to do your cleaning up for you.
Cursor attributes
--
four attributes appended to a cursor
name
%FOUND
-- error (not open or already closed), NULL (no fetch), TRUE,
FALSE)
%NOTFOUND
--
TRUE if record was not fetched successfully, FALSE otherwise.
%ISOPEN
--
TRUE if cursor is open, FALSE otherwise.
%ROWCOUNT
--
number of records fetched
e.g., EXIT WHEN employee_cur%NOTFOUND;
Processing Implicit Cursors
The same cursor attributes can be applied to the SQL cursor: e.g., SQL%NOTFOUND
SQL%NOTFOUND is not normally used with SELECT…INTO statements but used with Exception handler (NO_DATA_FOUND) -- we will talk about this in the later section.
EXAMPLE 1: Cursor Using LOOP … END LOOP (Explicit Cursor)
DECLARE
-- Declare variables to hold information about the students
majoring in 'History'.
v_StudentID students.id%TYPE;
v_FirstName
students.first_name%TYPE;
v_LastName
students.last_name%TYPE;
--
Cursor to retrieve the information about History
students
CURSOR c_HistoryStudents IS
SELECT
id, first_name,
last_name
FROM
students
WHERE
major =
'History';
BEGIN
OPEN c_HistoryStudents;
--
Open the cursor and initialize the active
set
LOOP
--
Retrieve information for the next
student
FETCH
c_HistoryStudents INTO v_StudentID, v_FirstName,
v_LastName;
EXIT
WHEN c_HistoryStudents%NOTFOUND;
--
Exit loop when there are no more rows to fetch
-- Process the fetched rows,
in this case sign up each student for History 301 by inserting
them
-- into the registered_students table. Record the first and last names in temp_table as
well.
INSERT
INTO registered_students (student_id, department,
course)
VALUES
(v_StudentID, 'HIS',
301);
INSERT
INTO temp_table (num_col,
char_col)
VALUES
(v_StudentID, v_FirstName || ' ' ||
v_LastName);
END
LOOP;
CLOSE
c_HistoryStudents; -- Free resources used by the cursor
COMMIT;
-- Commit our work
END;
/
EXAMPLE 2: Cursor using FOR LOOP … END LOOP (implicit Cursor)
DECLARE
-- Cursor to retrieve the
information about History
students
CURSOR c_HistoryStudents
IS
SELECT
id, first_name,
last_name
FROM
students
WHERE
major = 'History';
BEGIN
FOR
v_StudentData IN c_HistoryStudents
--
An implicit OPEN of c_HistoryStudents is done
here.
LOOP
-- v_StudentData is
implicitly Defined, and the scope of the variable is
within the loop
-- An implicit FETCH is
done here. Now process the fetched rows as in the
followings.
INSERT INTO registered_students (student_id,
department,
course)
VALUES
(v_StudentData.ID, 'HIS', 101);
INSERT INTO temp_table (num_col,
char_col)
VALUES
(v_StudentData.ID, v_StudentData.first_name || ' ' ||
v_StudentData.last_name);
-- Before the loop will continue, an
implicit Check of c_HistoryStudents is done
here.
END LOOP;
-- Now that
the loop is finished, an implicit CLOSE
of c_HistoryStudents is
done.
COMMIT;
END;
/
EXAMPLE 3: Cursor using Loop
The example below illustrates a cursor loop. It uses our example relation T1(e,f) whose tuples are pairs of integers. The program will delete every tuple whose first component is less than the second, and insert the reverse tuple into T1.
1) DECLARE /* Output variables to hold the result of the query: */ 2) a T1.e%TYPE; 3) b T1.f%TYPE; /* Cursor declaration: */ 4) CURSOR T1Cursor IS 5) SELECT e, f 6) FROM T1 7) WHERE e < f 8) FOR UPDATE; 9) BEGIN 10) OPEN T1Cursor; 11) LOOP /* Retrieve each row of the result of the above query into PL/SQL variables: */ 12) FETCH T1Cursor INTO a, b; /* If there are no more rows to fetch, exit the loop: */ 13) EXIT WHEN T1Cursor%NOTFOUND; /* Delete the current tuple: */ 14) DELETE FROM T1 WHERE CURRENT OF T1Cursor; /* Insert the reverse tuple: */ 15) INSERT INTO T1 VALUES(b, a); 16) END LOOP; /* Free cursor used by the query. */ 17) CLOSE T1Cursor; 18) END; 19) / 20) run;Here are explanations for the various lines of this program: