06 Cursors





PL/SQL Cursors


Oracle creates a memory area, known as context area, for processing an SQL statement, which contains all information needed for processing the statement, for example, number of rows processed, etc.

A cursor is a pointer to this context area. PL/SQL controls the context area through a cursor. A cursor holds the rows (one or more) returned by a SQL statement. The set of rows the cursor holds is referred to as the active set.

You can name a cursor so that it could be referred to in a program to fetch and process the rows returned by the SQL statement, one at a time. 

There are two types of cursors:

  • Implicit cursors
  • Explicit cursors


Implicit Cursors


Implicit cursors are automatically created by Oracle whenever an SQL statement is executed, when there is no explicit cursor for the statement. Programmers cannot control the implicit cursors and the information in it.

Whenever a DML statement (INSERT, UPDATE and DELETE) is issued, an implicit cursor is associated with this statement. For INSERT operations, the cursor holds the data that needs to be inserted. For UPDATE and DELETE operations, the cursor identifies the rows that would be affected.

In PL/SQL, you can refer to the most recent implicit cursor as the SQL cursor, which always has the attributes like %FOUND, %ISOPEN, %NOTFOUND, and %ROWCOUNT. 
The following table provides the description of the most used attributes:


AttributeDescription
%FOUNDReturns TRUE if an INSERT, UPDATE, or DELETE statement affected one or more rows or a SELECT INTO statement returned one or more rows. Otherwise, it returns FALSE.
%NOTFOUNDThe logical opposite of %FOUND. It returns TRUE if an INSERT, UPDATE, or DELETE statement affected no rows, or a SELECT INTO statement returned no rows. Otherwise, it returns FALSE.
%ISOPENAlways returns FALSE for implicit cursors, because Oracle closes the SQL cursor automatically after executing its associated SQL statement.
%ROWCOUNTReturns the number of rows affected by an INSERT, UPDATE, or DELETE statement, or returned by a SELECT INTO statement.

Any SQL cursor attribute will be accessed as sql%attribute_name as shown below in the example:


  1. DECLARE total_rows number(2); 
  2. BEGIN 
  3. UPDATE employees SET salary = salary + 500
  4. where salary<10000;

  5. IF sql%notfound THEN 
  6. dbms_output.put_line('no customers selected'); 
  7. ELSIF sql%found THEN 
  8. total_rows := sql%rowcount; 
  9. dbms_output.put_line( total_rows || ' customers selected '); 
  10. END IF; 
  11. END; 


Explicit Cursors


Explicit cursors are programmer defined cursors for gaining more control over the context area. An explicit cursor should be defined in the declaration section of the PL/SQL Block. It is created on a SELECT Statement which returns more than one row.



The syntax for creating an explicit cursor is:
CURSOR cursor_name IS SELECT STATEMENT;

Working with an explicit cursor involves four steps:
                  1. Declaring the cursor for initializing in the memory
                  2. Opening the cursor for allocating memory 
                  3. Fetching the cursor for retrieving data 
                  4. Closing the cursor to release allocated memory


Cursor Declaration
Declaring a cursor defines it with a name and a SELECT statement, for example

 CURSOR emp_cur IS SELECT employee_id,first_name,Last_name from employees;


Opening the cursor
Opening the cursor allocates memory for the cursor and makes it ready for fetching the rows returned by the SQL statement into it. For example the above declared cursor will be opened as follows:

OPEN emp_cur


Fetching data from the cursor

Fetching the cursor involves accessing one row at a time. For example we will fetch rows the above from the above opened cursor as follows:

FETCH emp_cur INTO e_id,f_name,l_name;

General Syntax to fetch records from a cursor is:

FETCH cursor_name INTO record_name;
OR 
FETCH cursor_name INTO variable_list;


Closing the cursor
Closing the cursor means releasing the memory allocated by him.
Example:

CLOSE emp_cur;

General Syntax to close a cursor is:

CLOSE cursor_name;


Current Row
When a cursor is opened, the first row becomes the current row. 
When the data is fetched it is copied to the record or variables and the logical pointer moves to the next row and it becomes the current row. 
On every fetch statement, the pointer moves to the next row. 
If you want to fetch after the last row, the program will throw an error. 
When there is more than one row in a cursor we can use loops along with explicit cursor attributes to fetch all the records.


Points to remember while fetching a row:
  • We can fetch the rows in a cursor to a PL/SQL Record or a list of variables created in the PL/SQL Block. 
  • If you are fetching a cursor to a PL/SQL Record, the record should have the same structure as the cursor.
  • If you are fetching a cursor to a list of variables, the variables should be listed in the same order in the fetch statement as the columns are present in the cursor.
General Form of using an explicit cursor is:

 DECLARE
    variables;
    records;
    create a cursor;
 BEGIN 
   OPEN cursor;
   FETCH cursor;
     process the records;
   CLOSE cursor;
 END;


Example 1:

  1. DECLARE 
  2. emp_rec employees%rowtype;
  3. CURSOR emp_cur IS 
  4. SELECT *
  5. FROM employees
  6. WHERE salary > 10; 
  7. BEGIN 
  8. OPEN emp_cur; 
  9. FETCH emp_cur INTO emp_rec; 
  10. dbms_output.put_line (emp_rec.first_name || ' ' || emp_rec.last_name); 
  11. CLOSE emp_cur; 
  12. END;

In the above example, :
  1. first we are creating a record emp_rec of the same structure as of table employees in line no
  2. Second, we are declaring a cursor emp_cur from a select query in line no 3 - 6. 
  3. Third, we are opening the cursor in the execution section in line no 8. 
  4. Fourth, we are fetching the cursor to the record in line no 9. 
  5. Fifth, we are displaying the first_name and last_name of the employee in the record emp_rec in line no 10. 
  6. Sixth, we are closing the cursor in line no 11.

Explicit Cursor Attributes?

Oracle provides some attributes known as Explicit Cursor Attributes to control the data processing while using cursors. We use these attributes to avoid errors while accessing cursors through OPEN, FETCH and CLOSE Statements.

When does an error occur while accessing an explicit cursor?
a) When we try to open a cursor which is not closed in the previous operation.
b) When we try to fetch a cursor after the last operation.

These are the attributes available to check the status of an explicit cursor.


Attributes

Return values

Example

%FOUND

TRUE, if fetch statement returns at least one row.

Cursor_name%FOUND

FALSE, if fetch statement doesn’t return a row.

%NOTFOUND

TRUE, , if fetch statement doesn’t return a row.Cursor_name%NOTFOUND
FALSE, if fetch statement returns at least one row.

%ROWCOUNT

The number of rows fetched by the fetch statement

Cursor_name%ROWCOUNT

If no row is returned, the PL/SQL statement returns an error.

%ISOPEN

TRUE, if the cursor is already open in the program

Cursor_name%ISNAME

FALSE, if the cursor is not opened in the program.




Cursor with a Simple Loop:



  1. DECLARE 
  2. CURSOR emp_cur IS 
  3. SELECT first_name, last_name, salary FROM employees; 
  4. emp_rec emp_cur%rowtype; 
  5. BEGIN 
  6. IF NOT sales_cur%ISOPEN THEN 
  7. OPEN sales_cur; 
  8. END IF; 
  9. LOOP 
  10. FETCH emp_cur INTO emp_rec; 
  11. EXIT WHEN emp_cur%NOTFOUND; 
  12. dbms_output.put_line(emp_cur.first_name || ' ' ||emp_cur.last_name 
  13. || ' ' ||emp_cur.salary); 
  14. END LOOP; 
  15. END;


In the above example we are using two cursor attributes %ISOPEN and %NOTFOUND. 
In line no 6, we are using the cursor attribute %ISOPEN to check if the cursor is open, if the condition is true the program does not open the cursor again, it directly moves to line no 9. 
In line no 11, we are using the cursor attribute %NOTFOUND to check whether the fetch returned any row. If there is no rows found the program would exit, a condition which exists when you fetch the cursor after the last row, if there is a row found the program continues.

We can use %FOUND in place of %NOTFOUND and vice versa. If we do so, we need to reverse the logic of the program. So use these attributes in appropriate instances.


Cursor with a While Loop:


Lets modify the above program to use while loop.

  1. DECLARE 
  2. CURSOR emp_cur IS 
  3. SELECT first_name, last_name, salary FROM employees; 
  4. emp_rec emp_cur%rowtype; 
  5. BEGIN 
  6. IF NOT sales_cur%ISOPEN THEN 
  7. OPEN sales_cur; 
  8. END IF; 
  9. FETCH sales_cur INTO sales_rec; 
  10. WHILE sales_cur%FOUND THEN 
  11. LOOP 
  12. dbms_output.put_line(emp_cur.first_name || ' ' ||emp_cur.last_name 
  13. || ' ' ||emp_cur.salary); 
  14. FETCH sales_cur INTO sales_rec; 
  15. END LOOP; 
  16. END;

In the above example, in line no 10 we are using %FOUND to evaluate if the first fetch statement in line no 9 returned a row, if true the program moves into the while loop. In the loop we use fetch statement again (line no 15) to process the next row. If the fetch statement is not executed once before the while loop the while condition will return false in the first instance and the while loop is skipped. In the loop, before fetching the record again, always process the record retrieved by the first fetch statement, else you will skip the first row.

Cursor with a FOR Loop:


When using FOR LOOP you need not declare a record or variables to store the cursor values, need not open, fetch and close the cursor. These functions are accomplished by the FOR LOOP automatically.

General Syntax for using FOR LOOP:

FOR record_name IN cusror_name 
LOOP 
    process the row...
END LOOP; 

Let’s use the above example to learn how to use for loops in cursors.

  1. DECLARE 
  2. CURSOR emp_cur IS 
  3. SELECT first_name, last_name, salary FROM employees; 
  4. emp_rec emp_cur%rowtype; 
  5. BEGIN 
  6. FOR emp_rec in sales_cur 
  7. LOOP 
  8. dbms_output.put_line(emp_cur.first_name || ' ' ||emp_cur.last_name 
  9. || ' ' ||emp_cur.salary); 
  10. END LOOP; 
  11. END;

In the above example, when the FOR loop is processed a record emp_rec of structure emp_cur gets created, the cursor is opened, the rows are fetched to the record emp_rec and the cursor is closed after the last row is processed. By using FOR Loop in your program, you can reduce the number of lines in the program.