Oracle Cursor Faqs With Examples

1. What is a Cursor in Oracle?

A cursor is a mechanism used by Oracle to process the result of a SQL statement, especially when working with rows returned by a query.

SQL Query
   ↓
Result Set
   ↓
Cursor
   ↓
Process rows

2. Why do we use Cursors?

Cursors are useful when you need to process query results row by row, perform calculations for each row, apply different logic to different rows, retrieve multiple rows in PL/SQL, or process records sequentially.

3. What are the types of Cursors?

Oracle cursors are mainly divided into two types: Implicit Cursor and Explicit Cursor.

             CURSORS
                |
        ┌───────┴───────┐
        ↓               ↓
    Implicit          Explicit
    Cursor            Cursor

4. What is an Implicit Cursor?

An implicit cursor is automatically created by Oracle whenever a SQL statement is executed. You do not need to declare, open, fetch from, or close it.

BEGIN
    UPDATE employees
    SET salary = salary + 1000
    WHERE department_id = 10;

    DBMS_OUTPUT.PUT_LINE(SQL%ROWCOUNT);
END;
/

5. What is an Explicit Cursor?

An explicit cursor is a cursor that the programmer explicitly declares and manages. It is useful when a query returns multiple rows and you want to process them individually.

DECLARE
    CURSOR emp_cursor IS
        SELECT employee_id, first_name
        FROM employees;
BEGIN
    OPEN emp_cursor;

    -- Fetch/process rows here

    CLOSE emp_cursor;
END;
/

6. What are the steps of an Explicit Cursor?

The traditional explicit cursor lifecycle has four steps: DECLARE, OPEN, FETCH, and CLOSE.

DOFC = Declare → Open → Fetch → Close

7. How do you Declare a Cursor?

Syntax:

CURSOR cursor_name IS
    SELECT statement;

Example:

DECLARE
    CURSOR emp_cursor IS
        SELECT employee_id, first_name
        FROM employees;
BEGIN
    NULL;
END;
/

8. How do you Open a Cursor?

Use OPEN cursor_name; to execute the cursor query and make the result set available for fetching.

9. What is FETCH?

FETCH retrieves rows from the cursor's result set. Each fetch retrieves the next row into the specified variables.

FETCH emp_cursor
INTO v_id, v_name;

10. How do you Close a Cursor?

Use CLOSE cursor_name; to release the cursor resources after processing is complete.

11. Complete Explicit Cursor Example

DECLARE
    CURSOR emp_cursor IS
        SELECT employee_id, first_name
        FROM employees;

v_id   employees.employee_id%TYPE;
v_name employees.first_name%TYPE;

BEGIN
OPEN emp_cursor;

LOOP
    FETCH emp_cursor INTO v_id, v_name;
    EXIT WHEN emp_cursor%NOTFOUND;

    DBMS_OUTPUT.PUT_LINE(v_id || ' - ' || v_name);
END LOOP;

CLOSE emp_cursor;

END;
/
DECLARE cursor
      ↓
OPEN cursor
      ↓
FETCH first row
      ↓
Process row
      ↓
FETCH next row
      ↓
Process row
      ↓
...
      ↓
%NOTFOUND = TRUE
      ↓
EXIT LOOP
      ↓
CLOSE cursor

12. What are Cursor Attributes?

Oracle provides cursor attributes to get information about cursor execution.

For implicit cursors:

SQL%FOUND
SQL%NOTFOUND
SQL%ROWCOUNT
SQL%ISOPEN

For explicit cursors:

cursor_name%FOUND
cursor_name%NOTFOUND
cursor_name%ROWCOUNT
cursor_name%ISOPEN

13. What is %FOUND?

%FOUND tells whether the most recent fetch or SQL operation affected or found a row.

14. What is %NOTFOUND?

%NOTFOUND becomes TRUE when the most recent fetch did not retrieve a row. It is commonly used to terminate a cursor loop.

LOOP
    FETCH emp_cursor INTO v_id, v_name;
    EXIT WHEN emp_cursor%NOTFOUND;
    DBMS_OUTPUT.PUT_LINE(v_name);
END LOOP;

15. What is %ROWCOUNT?

%ROWCOUNT tells how many rows have been fetched so far for an explicit cursor.

16. What is %ISOPEN?

%ISOPEN tells whether an explicit cursor is currently open.

17. Explicit Cursor Attributes — Summary

Attribute Meaning
%FOUND Last fetch found a row
%NOTFOUND Last fetch did not find a row
%ROWCOUNT Number of rows fetched so far
%ISOPEN Whether cursor is currently open

18. What is an Implicit Cursor Attribute?

For implicit SQL statements, Oracle uses the SQL cursor. The attributes are SQL%FOUND, SQL%NOTFOUND, SQL%ROWCOUNT, and SQL%ISOPEN.

19. Difference Between Implicit and Explicit Cursor

Implicit Cursor Explicit Cursor
Created automatically Declared by programmer
Oracle manages it Programmer manages it
No OPEN required Usually explicitly opened
No explicit FETCH required Explicit FETCH is possible
No explicit CLOSE required Programmer can explicitly close it
Uses SQL%... Uses cursor_name%...

20. What is a Cursor FOR LOOP?

A cursor FOR loop simplifies explicit cursor processing. Oracle automatically handles opening the cursor, fetching rows, and closing the cursor.

DECLARE
    CURSOR emp_cursor IS
        SELECT employee_id, first_name
        FROM employees;
BEGIN
    FOR emp IN emp_cursor LOOP
        DBMS_OUTPUT.PUT_LINE(emp.employee_id || ' - ' || emp.first_name);
    END LOOP;
END;
/

21. Why is Cursor FOR LOOP useful?

It makes code shorter and reduces mistakes. Instead of manually writing OPEN, FETCH, LOOP, EXIT, and CLOSE, you can use a Cursor FOR loop.

22. Cursor FOR LOOP Without Declaring a Cursor

BEGIN
    FOR emp IN (
        SELECT employee_id, first_name
        FROM employees
    )
    LOOP
        DBMS_OUTPUT.PUT_LINE(emp.employee_id || ' - ' || emp.first_name);
    END LOOP;
END;
/

23. What is a Parameterized Cursor?

A parameterized cursor accepts parameters when it is opened.

DECLARE
    CURSOR emp_cursor(p_dept_id NUMBER) IS
        SELECT employee_id, first_name
        FROM employees
        WHERE department_id = p_dept_id;
BEGIN
    FOR emp IN emp_cursor(10) LOOP
        DBMS_OUTPUT.PUT_LINE(emp.first_name);
    END LOOP;
END;
/

24. Why use Parameterized Cursors?

They allow the same cursor definition to be reused with different values.

25. What is a Nested Cursor?

A cursor can be used inside another loop or cursor-processing block. This is useful for department-by-department processing of employees.

BEGIN
    FOR dept IN (
        SELECT department_id
        FROM departments
    )
    LOOP
        FOR emp IN (
            SELECT employee_id, first_name
            FROM employees
            WHERE department_id = dept.department_id
        )
        LOOP
            DBMS_OUTPUT.PUT_LINE(emp.first_name);
        END LOOP;
    END LOOP;
END;
/

26. What happens if you FETCH from a closed cursor?

Oracle raises a cursor-related error such as INVALID_CURSOR.

27. What happens if you OPEN an already open cursor?

Opening an already open explicit cursor causes an error such as INVALID_CURSOR.

28. What happens if you CLOSE an already closed cursor?

Attempting to close a cursor that is not open causes an INVALID_CURSOR error.

29. Can a cursor return multiple rows?

Yes. Explicit cursors are commonly used to process multiple rows sequentially.

30. Can SELECT INTO use multiple rows?

A normal SELECT INTO expects a single row. If multiple rows are returned, Oracle raises TOO_MANY_ROWS.

31. Cursor and SELECT INTO Difference

SELECT INTO Cursor
Generally expects one row Can process multiple rows
Simple retrieval Useful for row-by-row processing
Multiple rows can cause TOO_MANY_ROWS Designed for multiple-row processing

32. What is a REF CURSOR?

A REF CURSOR is a cursor variable that can refer to different query result sets. It is commonly used when passing query results between PL/SQL programs and applications.

33. Strong REF CURSOR vs Weak REF CURSOR

A strong REF CURSOR has a specified return type. A weak REF CURSOR does not specify a fixed return type.

34. Implicit Cursor vs Cursor FOR LOOP

Do not confuse these concepts. An implicit cursor is automatically managed by Oracle for a SQL statement, while a Cursor FOR loop automatically manages the open, fetch, and close process for row-by-row iteration.

35. Complete Cursor Example for Exam

DECLARE
    CURSOR emp_cursor IS
        SELECT employee_id, first_name, salary
        FROM employees;

v_id     employees.employee_id%TYPE;
v_name   employees.first_name%TYPE;
v_salary employees.salary%TYPE;

BEGIN
OPEN emp_cursor;

LOOP
    FETCH emp_cursor INTO v_id, v_name, v_salary;
    EXIT WHEN emp_cursor%NOTFOUND;

    DBMS_OUTPUT.PUT_LINE(v_id || ' ' || v_name || ' ' || v_salary);
END LOOP;

CLOSE emp_cursor;

END;
/
DECLARE
   ↓
Define cursor
   ↓
OPEN
   ↓
FETCH
   ↓
Check %NOTFOUND
   ↓
Process row
   ↓
FETCH next row
   ↓
CLOSE

36. Frequently Asked Interview Questions

Question Answer
What is a cursor? A mechanism for processing SQL query results, especially row by row.
What are the two main types of cursors? Implicit and Explicit cursors.
Who manages an implicit cursor? Oracle automatically manages it.
Who manages an explicit cursor? The programmer controls its lifecycle.
What are the four steps of an explicit cursor? DECLARE → OPEN → FETCH → CLOSE
Which attribute checks whether a row was found? cursor_name%FOUND
Which attribute checks whether a row was not found? cursor_name%NOTFOUND
Which attribute gives the number of rows fetched? cursor_name%ROWCOUNT
Which attribute checks whether the cursor is open? cursor_name%ISOPEN

Important Exam Points

  1. A cursor is used to process SQL query results.
  2. There are two main types: Implicit and Explicit.
  3. Implicit cursors are automatically managed by Oracle.
  4. Explicit cursors are declared by the programmer.
  5. Traditional explicit cursor steps are DECLARE → OPEN → FETCH → CLOSE.
  6. %FOUND tells whether the last fetch found a row.
  7. %NOTFOUND tells whether the last fetch did not find a row.
  8. %ROWCOUNT tells the number of rows fetched so far.
  9. %ISOPEN tells whether an explicit cursor is open.
  10. Cursor FOR loops automatically manage opening, fetching, and closing.
  11. Parameterized cursors accept values when used or opened.
  12. SELECT INTO is generally for a single-row result; cursors are suitable for multiple-row processing.
  13. REF CURSOR is a cursor variable used to return or reference query result sets.

Easy Memory Trick

IMPLICIT
→ Oracle manages it

EXPLICIT
→ Programmer defines it

EXPLICIT CURSOR
→ DECLARE
→ OPEN
→ FETCH
→ CLOSE

ATTRIBUTES
→ FOUND
→ NOTFOUND
→ ROWCOUNT
→ ISOPEN

Oracle Implicit Cursor Faqs With Examples

1. What is an Implicit Cursor?

An implicit cursor is a cursor that Oracle automatically creates and manages whenever a SQL statement is executed.

Example:

BEGIN
    UPDATE employees
    SET salary = salary + 1000
    WHERE department_id = 10;


DBMS_OUTPUT.PUT_LINE(SQL%ROWCOUNT);


END;
/

Oracle automatically creates and manages the cursor for the UPDATE.

2. When does Oracle create an Implicit Cursor?

Oracle creates an implicit cursor when you execute SQL statements such as INSERT, UPDATE, DELETE, MERGE, and SELECT INTO.

UPDATE employees
SET salary = 50000
WHERE employee_id = 101;

3. Do we need to declare an Implicit Cursor?

No. You do not write CURSOR my_cursor IS ... for an implicit cursor. Oracle manages it automatically.

4. Do we need to OPEN an Implicit Cursor?

No. Oracle automatically opens and manages the implicit cursor when the SQL statement executes.

5. Do we need to FETCH an Implicit Cursor?

No. Oracle handles the processing automatically.

6. Do we need to CLOSE an Implicit Cursor?

No. Oracle automatically closes the implicit cursor after the SQL statement completes.

7. What is the name of the Implicit Cursor?

The implicit cursor is accessed using the special cursor name SQL.

SQL%FOUND
SQL%NOTFOUND
SQL%ROWCOUNT
SQL%ISOPEN

8. What are Implicit Cursor Attributes?

Attribute Meaning
SQL%FOUND Last SQL statement affected or found a row
SQL%NOTFOUND Last SQL statement affected or found no row
SQL%ROWCOUNT Number of rows affected
SQL%ISOPEN Whether the cursor is open

9. What is SQL%FOUND?

SQL%FOUND tells whether the most recently executed SQL statement affected or returned at least one row, as applicable.

BEGIN
    UPDATE employees
    SET salary = salary + 1000
    WHERE employee_id = 101;


IF SQL%FOUND THEN
    DBMS_OUTPUT.PUT_LINE('Employee updated');
END IF;


END;
/

10. What is SQL%NOTFOUND?

SQL%NOTFOUND becomes TRUE when the most recent SQL operation did not affect or return a row, as applicable.

BEGIN
    UPDATE employees
    SET salary = salary + 1000
    WHERE employee_id = 9999;


IF SQL%NOTFOUND THEN
    DBMS_OUTPUT.PUT_LINE('No employee updated');
END IF;


END;
/

11. What is SQL%ROWCOUNT?

SQL%ROWCOUNT tells how many rows were affected by the most recent DML statement.

BEGIN
    UPDATE employees
    SET salary = salary + 1000
    WHERE department_id = 10;


DBMS_OUTPUT.PUT_LINE(
    'Rows updated: ' || SQL%ROWCOUNT
);


END;
/

12. What is SQL%ISOPEN?

SQL%ISOPEN tells whether the implicit SQL cursor is open. For an implicit cursor, Oracle manages its lifecycle, and after the statement completes, SQL%ISOPEN is always FALSE.

BEGIN
    UPDATE employees
    SET salary = salary + 1000
    WHERE employee_id = 101;


IF SQL%ISOPEN THEN
    DBMS_OUTPUT.PUT_LINE('Cursor is open');
ELSE
    DBMS_OUTPUT.PUT_LINE('Cursor is closed');
END IF;


END;
/

Exam point: For an implicit cursor, SQL%ISOPEN is always FALSE after the statement completes.

13. Implicit Cursor with INSERT

BEGIN
    INSERT INTO employees
    (employee_id, first_name, salary)
    VALUES
    (101, 'Ravi', 50000);


DBMS_OUTPUT.PUT_LINE(
    'Rows inserted: ' || SQL%ROWCOUNT
);


END;
/

14. Implicit Cursor with UPDATE

BEGIN
    UPDATE employees
    SET salary = 60000
    WHERE employee_id = 101;


DBMS_OUTPUT.PUT_LINE(
    'Rows updated: ' || SQL%ROWCOUNT
);


END;
/

15. Implicit Cursor with DELETE

BEGIN
    DELETE FROM employees
    WHERE employee_id = 101;


DBMS_OUTPUT.PUT_LINE(
    'Rows deleted: ' || SQL%ROWCOUNT
);


END;
/

16. Implicit Cursor with MERGE

BEGIN
    MERGE INTO employees e
    USING new_employees n
    ON (e.employee_id = n.employee_id)
    WHEN MATCHED THEN
        UPDATE SET e.salary = n.salary
    WHEN NOT MATCHED THEN
        INSERT (employee_id, first_name, salary)
        VALUES (n.employee_id, n.first_name, n.salary);


DBMS_OUTPUT.PUT_LINE(
    'Rows affected: ' || SQL%ROWCOUNT
);


END;
/

17. Implicit Cursor with SELECT INTO

DECLARE
    v_name employees.first_name%TYPE;
BEGIN
    SELECT first_name
    INTO v_name
    FROM employees
    WHERE employee_id = 101;


DBMS_OUTPUT.PUT_LINE(v_name);


END;
/

Oracle automatically manages the cursor for the SELECT INTO.

18. What happens if SELECT INTO finds no row?

Oracle raises the predefined exception NO_DATA_FOUND.

DECLARE
    v_name employees.first_name%TYPE;
BEGIN
    SELECT first_name
    INTO v_name
    FROM employees
    WHERE employee_id = 9999;


DBMS_OUTPUT.PUT_LINE(v_name);


EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('Employee not found');
END;
/

19. What happens if SELECT INTO finds multiple rows?

Oracle raises TOO_MANY_ROWS.

DECLARE
    v_name employees.first_name%TYPE;
BEGIN
    SELECT first_name
    INTO v_name
    FROM employees
    WHERE department_id = 10;
END;
/

20. How does SQL%ROWCOUNT work with DELETE?

BEGIN
    DELETE FROM employees
    WHERE department_id = 20;


DBMS_OUTPUT.PUT_LINE(
    'Deleted: ' || SQL%ROWCOUNT
);


END;
/

21. What happens to SQL%ROWCOUNT after another SQL statement?

Implicit cursor attributes refer to the most recently executed SQL statement.

BEGIN
    UPDATE employees
    SET salary = salary + 1000
    WHERE department_id = 10;


DBMS_OUTPUT.PUT_LINE(SQL%ROWCOUNT);

DELETE FROM employees
WHERE employee_id = 101;

DBMS_OUTPUT.PUT_LINE(SQL%ROWCOUNT);


END;
/

The first SQL%ROWCOUNT belongs to the UPDATE, and the second belongs to the DELETE.

22. Implicit Cursor Example with IF

BEGIN
    UPDATE employees
    SET salary = salary + 5000
    WHERE employee_id = 101;


IF SQL%ROWCOUNT = 1 THEN
    DBMS_OUTPUT.PUT_LINE('One employee updated');
ELSIF SQL%ROWCOUNT = 0 THEN
    DBMS_OUTPUT.PUT_LINE('No employee found');
END IF;


END;
/

23. Implicit Cursor Example with ROWCOUNT

DECLARE
    v_count NUMBER;
BEGIN
    DELETE FROM employees
    WHERE department_id = 30;


v_count := SQL%ROWCOUNT;

DBMS_OUTPUT.PUT_LINE(
    'Number of rows deleted = ' || v_count
);


END;
/

24. Why save SQL%ROWCOUNT in a variable?

Because another SQL statement can change the implicit cursor information.

DECLARE
    v_count NUMBER;
BEGIN
    UPDATE employees
    SET salary = salary + 1000
    WHERE department_id = 10;

    v_count := SQL%ROWCOUNT;

    DBMS_OUTPUT.PUT_LINE(
        'Rows updated = ' || v_count
    );
END;
/

25. Implicit Cursor vs Explicit Cursor

Implicit Cursor Explicit Cursor
Created automatically Declared by programmer
Oracle manages it Programmer controls it
No explicit OPEN Usually explicitly opened
No explicit FETCH Can explicitly fetch
No explicit CLOSE Explicitly closed when manually managed
Uses SQL%... Uses cursor_name%...

26. Implicit Cursor vs Cursor FOR LOOP

An implicit cursor is automatically managed by Oracle for SQL statements, while a Cursor FOR LOOP automatically manages OPEN, FETCH, and CLOSE for row-by-row processing.

27. Do we use OPEN, FETCH, and CLOSE with an Implicit Cursor?

No. You do not normally write OPEN SQL, FETCH SQL, or CLOSE SQL. Oracle handles these operations.

28. What is the main advantage of an Implicit Cursor?

It is simple and automatic.

29. What is the disadvantage?

Implicit cursors provide less manual control over multi-row processing than explicit cursors.

30. Complete Example

DECLARE
    v_count NUMBER;
BEGIN
    UPDATE employees
    SET salary = salary + 2000
    WHERE department_id = 10;


v_count := SQL%ROWCOUNT;

IF SQL%FOUND THEN
    DBMS_OUTPUT.PUT_LINE(
        'Employees updated: ' || v_count
    );
ELSE
    DBMS_OUTPUT.PUT_LINE(
        'No employees updated'
    );
END IF;


END;
/

Step-by-step:

UPDATE executed
      ↓
Oracle creates implicit cursor
      ↓
Oracle processes the statement
      ↓
SQL%ROWCOUNT gives affected rows
      ↓
SQL%FOUND checks whether rows were affected
      ↓
Oracle manages the cursor automatically

31. Frequently Asked Questions

Question Answer
What is an implicit cursor? A cursor automatically created and managed by Oracle for SQL statements.
Who manages an implicit cursor? Oracle.
Do we declare an implicit cursor? No.
Do we explicitly open an implicit cursor? No.
Do we explicitly fetch from an implicit cursor? No.
Do we explicitly close an implicit cursor? No.
What is the implicit cursor name? SQL
What are the implicit cursor attributes? SQL%FOUND, SQL%NOTFOUND, SQL%ROWCOUNT, SQL%ISOPEN
Which attribute tells how many rows were affected? SQL%ROWCOUNT
Which attribute tells whether a row was affected or found? SQL%FOUND
Which attribute tells that no row was affected or found? SQL%NOTFOUND
What is the value of SQL%ISOPEN after the statement completes? FALSE

Important Exam Points

  1. Implicit cursor = automatically managed by Oracle.
  2. The implicit cursor is referenced using SQL.
  3. No explicit DECLARE, OPEN, FETCH, or CLOSE is required.
  4. Commonly associated with INSERT, UPDATE, DELETE, MERGE, and SELECT INTO.
  5. SQL%FOUND means a row was affected or found.
  6. SQL%NOTFOUND means no row was affected or found.
  7. SQL%ROWCOUNT means the number of rows affected or fetched, as applicable.
  8. SQL%ISOPEN is FALSE after the statement completes.
  9. SELECT INTO generally expects exactly one row.
  10. No row from SELECT INTO causes NO_DATA_FOUND.
  11. Multiple rows from SELECT INTO cause TOO_MANY_ROWS.
  12. Cursor attributes refer to the most recently executed relevant SQL statement.

Easy Memory Trick

IMPLICIT CURSOR
       ↓
Oracle manages it
       ↓
No DECLARE
No OPEN
No FETCH
No CLOSE
       ↓
Use SQL% attributes

FOUND     → Row found
NOTFOUND  → No row
ROWCOUNT  → Number of rows
ISOPEN    → Cursor status

Implicit Cursor = Oracle manages the cursor; you mainly use SQL%FOUND, SQL%NOTFOUND, SQL%ROWCOUNT, and SQL%ISOPEN to check what happened.

Oracle Explicit Cursor Faqs With Examples

1. What is an Explicit Cursor?

An explicit cursor is a cursor that is declared and controlled by the programmer in PL/SQL. It is mainly used when a query returns multiple rows and you want to process those rows one by one.

Basic flow:

DECLARE
   ↓
OPEN
   ↓
FETCH
   ↓
PROCESS
   ↓
CLOSE

Example:

DECLARE
    CURSOR emp_cursor IS
        SELECT employee_id, first_name
        FROM employees;


v_id   employees.employee_id%TYPE;
v_name employees.first_name%TYPE;


BEGIN
OPEN emp_cursor;


LOOP
    FETCH emp_cursor INTO v_id, v_name;

    EXIT WHEN emp_cursor%NOTFOUND;

    DBMS_OUTPUT.PUT_LINE(
        v_id || ' - ' || v_name
    );
END LOOP;

CLOSE emp_cursor;


END;
/

2. Why do we use Explicit Cursors?

Explicit cursors are useful when you need to process multiple rows one by one, apply different logic to individual rows, perform calculations for each row, update or process data based on each fetched record, or have more control over the cursor lifecycle.

3. What are the steps of an Explicit Cursor?

There are traditionally four steps: DECLARE, OPEN, FETCH, and CLOSE.

Easy memory trick:

DOFC = Declare → Open → Fetch → Close

4. How do you declare an Explicit Cursor?

Syntax:

CURSOR cursor_name IS
    SELECT_statement;

Example:

DECLARE
    CURSOR emp_cursor IS
        SELECT employee_id, first_name, salary
        FROM employees;
BEGIN
    NULL;
END;
/

5. What is the OPEN operation?

OPEN activates the explicit cursor and makes its result set available for fetching.

OPEN emp_cursor;

6. What is FETCH?

FETCH retrieves the next row from the cursor result set into variables or a record.

FETCH emp_cursor
INTO v_id, v_name, v_salary;

7. What is CLOSE?

CLOSE closes the cursor after processing is complete.

CLOSE emp_cursor;

8. Complete Explicit Cursor Example

DECLARE
    CURSOR emp_cursor IS
        SELECT employee_id, first_name, salary
        FROM employees;


v_id     employees.employee_id%TYPE;
v_name   employees.first_name%TYPE;
v_salary employees.salary%TYPE;


BEGIN
OPEN emp_cursor;


LOOP
    FETCH emp_cursor
    INTO v_id, v_name, v_salary;

    EXIT WHEN emp_cursor%NOTFOUND;

    DBMS_OUTPUT.PUT_LINE(
        v_id || ' - ' ||
        v_name || ' - ' ||
        v_salary
    );
END LOOP;

CLOSE emp_cursor;


END;
/

Execution flow:

DECLARE cursor
      ↓
OPEN cursor
      ↓
FETCH row 1
      ↓
Process row 1
      ↓
FETCH row 2
      ↓
Process row 2
      ↓
...
      ↓
%NOTFOUND = TRUE
      ↓
EXIT LOOP
      ↓
CLOSE cursor

9. What are Explicit Cursor Attributes?

Oracle provides four important attributes:

cursor_name%FOUND
cursor_name%NOTFOUND
cursor_name%ROWCOUNT
cursor_name%ISOPEN

10. What is %FOUND?

%FOUND tells whether the most recent fetch successfully retrieved a row.

Example:

DECLARE
    CURSOR emp_cursor IS
        SELECT employee_id, first_name
        FROM employees;


v_id   employees.employee_id%TYPE;
v_name employees.first_name%TYPE;


BEGIN
OPEN emp_cursor;


FETCH emp_cursor INTO v_id, v_name;

IF emp_cursor%FOUND THEN
    DBMS_OUTPUT.PUT_LINE('Row found');
END IF;

CLOSE emp_cursor;


END;
/

11. What is %NOTFOUND?

%NOTFOUND becomes TRUE when the most recent FETCH does not retrieve a row. It is commonly used to stop a cursor loop.

LOOP
    FETCH emp_cursor INTO v_id, v_name;

    EXIT WHEN emp_cursor%NOTFOUND;

    DBMS_OUTPUT.PUT_LINE(v_name);
END LOOP;

Important: %NOTFOUND is commonly used with EXIT WHEN in explicit cursor loops.

12. What is %ROWCOUNT?

%ROWCOUNT tells how many rows have been fetched so far.

FETCH emp_cursor INTO v_id, v_name;

DBMS_OUTPUT.PUT_LINE(
    'Rows fetched: ' || emp_cursor%ROWCOUNT
);

After the first fetch, the value is 1; after the second fetch, it is 2; and so on.

13. What is %ISOPEN?

%ISOPEN tells whether an explicit cursor is currently open.

OPEN emp_cursor;

IF emp_cursor%ISOPEN THEN
    DBMS_OUTPUT.PUT_LINE('Cursor is open');
END IF;

CLOSE emp_cursor;

After CLOSE, emp_cursor%ISOPEN = FALSE.

14. Explicit Cursor Attributes — Summary

Attribute Meaning
%FOUND Last fetch successfully found a row
%NOTFOUND Last fetch did not find a row
%ROWCOUNT Number of rows fetched so far
%ISOPEN Whether cursor is open

15. Why is %NOTFOUND important?

When there are no more rows, %NOTFOUND becomes TRUE and the loop exits.

FETCH
 ↓
No row
 ↓
%NOTFOUND = TRUE
 ↓
EXIT

16. What is the correct position of %NOTFOUND?

The common pattern is to check it after the FETCH.

LOOP
    FETCH emp_cursor INTO v_id, v_name;

    EXIT WHEN emp_cursor%NOTFOUND;

    -- Process fetched row
END LOOP;

17. Can an Explicit Cursor return multiple rows?

Yes. This is one of its main uses.

18. Can an Explicit Cursor return zero rows?

Yes. If the query returns no rows, the cursor can still be opened, and the first fetch will indicate that no row was returned.

19. Explicit Cursor with WHERE Condition

DECLARE
    CURSOR emp_cursor IS
        SELECT employee_id, first_name, salary
        FROM employees
        WHERE department_id = 10;
BEGIN
    FOR emp IN emp_cursor LOOP
        DBMS_OUTPUT.PUT_LINE(
            emp.employee_id || ' - ' || emp.first_name
        );
    END LOOP;
END;
/

20. What is a Parameterized Explicit Cursor?

A parameterized cursor accepts parameters when it is opened or used.

DECLARE
    CURSOR emp_cursor(p_dept_id NUMBER) IS
        SELECT employee_id, first_name
        FROM employees
        WHERE department_id = p_dept_id;

    v_id   employees.employee_id%TYPE;
    v_name employees.first_name%TYPE;
BEGIN
    OPEN emp_cursor(10);

    LOOP
        FETCH emp_cursor INTO v_id, v_name;
        EXIT WHEN emp_cursor%NOTFOUND;

        DBMS_OUTPUT.PUT_LINE(v_id || ' - ' || v_name);
    END LOOP;

    CLOSE emp_cursor;
END;
/

21. Why use a Parameterized Cursor?

It allows the same cursor definition to be reused with different values.

22. Can we create multiple Explicit Cursors?

Yes. Each cursor can have its own query and lifecycle.

23. What is a Cursor FOR LOOP?

A Cursor FOR LOOP is a convenient way to process cursor rows. Oracle/PLSQL automatically handles OPEN, FETCH, and CLOSE for the loop.

DECLARE
    CURSOR emp_cursor IS
        SELECT employee_id, first_name
        FROM employees;
BEGIN
    FOR emp IN emp_cursor LOOP
        DBMS_OUTPUT.PUT_LINE(
            emp.employee_id || ' - ' || emp.first_name
        );
    END LOOP;
END;
/

24. Do we need OPEN, FETCH, and CLOSE with a Cursor FOR LOOP?

No. The Cursor FOR LOOP automatically handles the cursor lifecycle.

25. Explicit Cursor vs Cursor FOR LOOP

Manual Explicit Cursor Cursor FOR LOOP
OPEN emp_cursor No OPEN needed
FETCH emp_cursor INTO ... No FETCH needed
EXIT WHEN emp_cursor%NOTFOUND No EXIT WHEN needed
CLOSE emp_cursor No CLOSE needed

26. Can we use a query directly in a Cursor FOR LOOP?

Yes. You do not even need to declare a named cursor.

BEGIN
    FOR emp IN (
        SELECT employee_id, first_name
        FROM employees
    )
    LOOP
        DBMS_OUTPUT.PUT_LINE(
            emp.employee_id || ' - ' || emp.first_name
        );
    END LOOP;
END;
/

27. What happens if you FETCH from a closed cursor?

Oracle raises a cursor-related error such as INVALID_CURSOR.

28. What happens if you OPEN an already open cursor?

An already open explicit cursor cannot be opened again without first closing it. This causes an error such as INVALID_CURSOR.

29. What happens if you CLOSE an already closed cursor?

Attempting to close an already closed cursor raises a cursor error such as INVALID_CURSOR.

30. What happens if the cursor query returns no rows?

The cursor can be opened, but the fetch indicates that no row was returned.

31. Explicit Cursor with Record

DECLARE
    CURSOR emp_cursor IS
        SELECT employee_id, first_name, salary
        FROM employees;


v_emp emp_cursor%ROWTYPE;


BEGIN
OPEN emp_cursor;


LOOP
    FETCH emp_cursor INTO v_emp;
    EXIT WHEN emp_cursor%NOTFOUND;

    DBMS_OUTPUT.PUT_LINE(
        v_emp.employee_id || ' - ' ||
        v_emp.first_name || ' - ' ||
        v_emp.salary
    );
END LOOP;

CLOSE emp_cursor;


END;
/

32. What is %ROWTYPE with a Cursor?

cursor_name%ROWTYPE creates a record that can hold one row returned by the cursor.

33. Can an Explicit Cursor be used for UPDATE?

Yes. You can fetch rows and then perform operations based on the fetched values.

34. What is FOR UPDATE in an Explicit Cursor?

FOR UPDATE can be used when you intend to update or delete the rows selected by the cursor.

35. What is WHERE CURRENT OF?

WHERE CURRENT OF allows you to update or delete the current row of a cursor declared with FOR UPDATE.

Easy memory trick:

FOR UPDATE
    ↓
Lock selected rows for update
    ↓
WHERE CURRENT OF
    ↓
Modify current cursor row

36. Explicit Cursor vs Implicit Cursor

Explicit Cursor Implicit Cursor
Programmer declares it Oracle creates it
Programmer controls lifecycle Oracle manages lifecycle
Uses cursor_name%... Uses SQL%...
Suitable for controlled multi-row processing Convenient for single SQL statements

37. Explicit Cursor vs SELECT INTO

SELECT INTO: Suitable for single-row retrieval.

Explicit Cursor: Suitable for multiple rows and row-by-row processing.

38. Complete Example with All Four Steps

DECLARE
    CURSOR emp_cursor IS
        SELECT employee_id, first_name, salary
        FROM employees
        WHERE department_id = 10;


v_emp emp_cursor%ROWTYPE;


BEGIN
OPEN emp_cursor;


LOOP
    FETCH emp_cursor INTO v_emp;
    EXIT WHEN emp_cursor%NOTFOUND;

    DBMS_OUTPUT.PUT_LINE(
        'ID: ' || v_emp.employee_id ||
        ', Name: ' || v_emp.first_name ||
        ', Salary: ' || v_emp.salary
    );
END LOOP;

CLOSE emp_cursor;


END;
/

39. Frequently Asked Interview Questions

Question Answer
What is an explicit cursor? A cursor explicitly declared and controlled by the programmer to process rows one by one.
What are the four steps? DECLARE → OPEN → FETCH → CLOSE
Which command opens a cursor? OPEN cursor_name;
Which command retrieves a row? FETCH cursor_name INTO variables;
Which command closes a cursor? CLOSE cursor_name;
What does %FOUND mean? The last fetch successfully retrieved a row.
What does %NOTFOUND mean? The last fetch did not retrieve a row.
What does %ROWCOUNT mean? Number of rows fetched so far.
What does %ISOPEN mean? Whether the explicit cursor is currently open.
What happens if you fetch from a closed cursor? An INVALID_CURSOR error occurs.

Important Exam Points

  1. Explicit cursor = programmer-controlled cursor.
  2. Mainly used for multiple-row processing.
  3. Four traditional steps: DECLARE → OPEN → FETCH → CLOSE.
  4. OPEN activates the cursor.
  5. FETCH retrieves the next row.
  6. CLOSE closes the cursor.
  7. %FOUND means the last fetch found a row.
  8. %NOTFOUND means the last fetch found no row.
  9. %ROWCOUNT means number of rows fetched.
  10. %ISOPEN means cursor open status.
  11. cursor_name%ROWTYPE can represent one cursor row.
  12. Parameterized cursors accept input values.
  13. Cursor FOR loops automatically manage open, fetch, and close.
  14. FOR UPDATE is used when selected rows need to be locked for update/delete processing.
  15. WHERE CURRENT OF operates on the current row of a FOR UPDATE cursor.
  16. An explicit cursor uses cursor_name%attribute, unlike an implicit cursor, which uses SQL%attribute.

One-Line Memory Trick

EXPLICIT CURSOR
      ↓
DECLARE
      ↓
OPEN
      ↓
FETCH
      ↓
%NOTFOUND?
   ↓       ↓
  NO      YES
  ↓        ↓
PROCESS   EXIT
           ↓
         CLOSE

Explicit Cursor = Programmer controls the cursor to process rows one by one.

Oracle Cursor Attributes FAQs with Examples

1. What are Cursor Attributes?

Cursor attributes are properties that tell you what happened during cursor processing.

For an implicit cursor:

SQL%FOUND
SQL%NOTFOUND
SQL%ROWCOUNT
SQL%ISOPEN

For an explicit cursor:

emp_cursor%FOUND
emp_cursor%NOTFOUND
emp_cursor%ROWCOUNT
emp_cursor%ISOPEN

2. What are the four main Cursor Attributes?

Attribute Meaning
%FOUND Indicates whether a row was found or affected
%NOTFOUND Indicates whether no row was found or affected
%ROWCOUNT Number of rows affected or fetched
%ISOPEN Indicates whether an explicit cursor is open

Memory trick: F-N-R-I = Found, Not Found, Row Count, Is Open

3. What is %FOUND?

%FOUND tells whether the most recent cursor operation successfully found or affected a row, as applicable.

Explicit cursor example:

DECLARE
    CURSOR emp_cursor IS
        SELECT employee_id, first_name
        FROM employees;
v_id   employees.employee_id%TYPE;
v_name employees.first_name%TYPE;
BEGIN
OPEN emp_cursor;
FETCH emp_cursor INTO v_id, v_name;
IF emp_cursor%FOUND THEN
    DBMS_OUTPUT.PUT_LINE('Row found');
END IF;
CLOSE emp_cursor;
END;
/

If the fetch retrieves a row, the output is:

Row found

4. What is SQL%FOUND?

For an implicit cursor, use SQL%FOUND.

Example:

BEGIN
    UPDATE employees
    SET salary = salary + 1000
    WHERE employee_id = 101;


IF SQL%FOUND THEN
    DBMS_OUTPUT.PUT_LINE('Employee updated');
END IF;


END;
/

If employee 101 was updated, %FOUND is TRUE.

5. What is %NOTFOUND?

%NOTFOUND indicates that the most recent cursor operation did not find or affect a row, as applicable. It is particularly important when processing an explicit cursor.

Example:

DECLARE
    CURSOR emp_cursor IS
        SELECT employee_id, first_name
        FROM employees;


v_id   employees.employee_id%TYPE;
v_name employees.first_name%TYPE;


BEGIN
OPEN emp_cursor;


LOOP
    FETCH emp_cursor INTO v_id, v_name;

    EXIT WHEN emp_cursor%NOTFOUND;

    DBMS_OUTPUT.PUT_LINE(v_name);
END LOOP;

CLOSE emp_cursor;


END;
/

Flow:

FETCH
  ↓
Row found?
  ↓
YES → Process row
  ↓
FETCH again
  ↓
No row
  ↓
%NOTFOUND = TRUE
  ↓
EXIT

6. What is SQL%NOTFOUND?

For an implicit cursor, SQL%NOTFOUND can be used after DML to determine that no rows were affected.

Example:

BEGIN
    DELETE FROM employees
    WHERE employee_id = 9999;


IF SQL%NOTFOUND THEN
    DBMS_OUTPUT.PUT_LINE('No employee deleted');
END IF;


END;
/

If employee 9999 does not exist, the output is:

No employee deleted

7. What is %ROWCOUNT?

%ROWCOUNT tells the number of rows processed. For an explicit cursor, it gives the number of rows fetched so far.

Example:

DECLARE
    CURSOR emp_cursor IS
        SELECT employee_id, first_name
        FROM employees;


v_id   employees.employee_id%TYPE;
v_name employees.first_name%TYPE;


BEGIN
OPEN emp_cursor;


LOOP
    FETCH emp_cursor INTO v_id, v_name;

    EXIT WHEN emp_cursor%NOTFOUND;

    DBMS_OUTPUT.PUT_LINE(
        'Rows fetched: ' || emp_cursor%ROWCOUNT
    );
END LOOP;

CLOSE emp_cursor;


END;
/

The values increase as rows are fetched:

Rows fetched: 1
Rows fetched: 2
Rows fetched: 3
...

8. What is SQL%ROWCOUNT?

For an implicit cursor, SQL%ROWCOUNT tells the number of rows affected by the most recent DML statement.

Example:

BEGIN
    UPDATE employees
    SET salary = salary + 2000
    WHERE department_id = 10;


DBMS_OUTPUT.PUT_LINE(
    'Rows updated: ' || SQL%ROWCOUNT
);


END;
/

If 10 employees are updated, the output is:

Rows updated: 10

9. What is %ISOPEN?

%ISOPEN tells whether an explicit cursor is currently open.

Example:

DECLARE
    CURSOR emp_cursor IS
        SELECT employee_id
        FROM employees;
BEGIN
    OPEN emp_cursor;


IF emp_cursor%ISOPEN THEN
    DBMS_OUTPUT.PUT_LINE('Cursor is open');
END IF;

CLOSE emp_cursor;


END;
/

Output:

Cursor is open

10. What is SQL%ISOPEN?

For an implicit cursor, Oracle manages the cursor automatically.

Example:

BEGIN
    UPDATE employees
    SET salary = salary + 1000
    WHERE employee_id = 101;


IF SQL%ISOPEN THEN
    DBMS_OUTPUT.PUT_LINE('Open');
ELSE
    DBMS_OUTPUT.PUT_LINE('Closed');
END IF;


END;
/

After the statement completes, the implicit cursor is closed, so the result is:

Closed

Exam point: SQL%ISOPEN is always FALSE after the implicit SQL statement has completed.

11. Difference between implicit and explicit cursor attributes

Implicit Cursor Explicit Cursor
SQL%FOUND emp_cursor%FOUND
SQL%NOTFOUND emp_cursor%NOTFOUND
SQL%ROWCOUNT emp_cursor%ROWCOUNT
SQL%ISOPEN emp_cursor%ISOPEN

12. Example of all four explicit cursor attributes

DECLARE
    CURSOR emp_cursor IS
        SELECT employee_id, first_name
        FROM employees;


v_id   employees.employee_id%TYPE;
v_name employees.first_name%TYPE;
BEGIN
OPEN emp_cursor;
IF emp_cursor%ISOPEN THEN
    DBMS_OUTPUT.PUT_LINE('Cursor is open');
END IF;
LOOP
    FETCH emp_cursor INTO v_id, v_name;

    IF emp_cursor%FOUND THEN
        DBMS_OUTPUT.PUT_LINE('Employee: ' || v_name);
        DBMS_OUTPUT.PUT_LINE('Rows fetched: ' || emp_cursor%ROWCOUNT);
    END IF;

    EXIT WHEN emp_cursor%NOTFOUND;
END LOOP;
CLOSE emp_cursor;
IF NOT emp_cursor%ISOPEN THEN
    DBMS_OUTPUT.PUT_LINE('Cursor is closed');
END IF;
END;
/

This demonstrates %ISOPEN, %FOUND, %ROWCOUNT, and %NOTFOUND.

13. Why is %NOTFOUND usually checked after FETCH?

Because the cursor needs to attempt a fetch before it can determine whether another row exists.

LOOP
    FETCH emp_cursor INTO v_id, v_name;
    EXIT WHEN emp_cursor%NOTFOUND;
    DBMS_OUTPUT.PUT_LINE(v_name);
END LOOP;

14. What happens to %ROWCOUNT after each FETCH?

For an explicit cursor:

First successful FETCH  → %ROWCOUNT = 1
Second successful FETCH → %ROWCOUNT = 2
Third successful FETCH  → %ROWCOUNT = 3

15. Example of %ROWCOUNT with INSERT

BEGIN
    INSERT INTO employees
    (employee_id, first_name, salary)
    VALUES
    (101, 'Ravi', 50000);
DBMS_OUTPUT.PUT_LINE(
    'Rows inserted: ' || SQL%ROWCOUNT
);
END;
/

Output:

Rows inserted: 1

16. Example of %ROWCOUNT with DELETE

BEGIN
    DELETE FROM employees
    WHERE department_id = 20;
DBMS_OUTPUT.PUT_LINE(
    'Rows deleted: ' || SQL%ROWCOUNT
);


END;
/

If 7 rows were deleted, the output is:

Rows deleted: 7

17. Example of %ROWCOUNT with UPDATE

BEGIN
    UPDATE employees
    SET salary = 60000
    WHERE department_id = 10;


IF SQL%ROWCOUNT > 0 THEN
    DBMS_OUTPUT.PUT_LINE(SQL%ROWCOUNT || ' employees updated');
ELSE
    DBMS_OUTPUT.PUT_LINE('No employees updated');
END IF;


END;
/

18. Can %ROWCOUNT be zero?

Yes.

BEGIN
    DELETE FROM employees
    WHERE employee_id = 9999;

    DBMS_OUTPUT.PUT_LINE(SQL%ROWCOUNT);
END;
/

If the employee does not exist, the output is:

0

19. Can %FOUND and %NOTFOUND both be TRUE?

No. For a given cursor state, they represent opposite conditions.

Conceptually:

%FOUND    = TRUE  → row found
%NOTFOUND = TRUE  → row not found

20. What happens to cursor attributes after another SQL statement?

Cursor attributes can be affected by subsequent SQL operations.

Best practice: save the value if you need it later.

DECLARE
    v_count NUMBER;
BEGIN
    UPDATE employees
    SET salary = salary + 1000
    WHERE department_id = 10;


v_count := SQL%ROWCOUNT;

DBMS_OUTPUT.PUT_LINE('Employees updated: ' || v_count);


END;
/

21. What happens if %NOTFOUND is used before the first FETCH?

For an explicit cursor, you should not use %NOTFOUND as the loop condition before the first fetch. Use the standard fetch-then-exit pattern.

22. What happens if you use %FOUND after a failed FETCH?

After a fetch that does not retrieve a row:

%FOUND = FALSE
%NOTFOUND = TRUE

23. Cursor Attributes with Cursor FOR LOOP

In a Cursor FOR LOOP, you generally do not need to manually check %FOUND or %NOTFOUND.

BEGIN
    FOR emp IN (
        SELECT employee_id, first_name
        FROM employees
    )
    LOOP
        DBMS_OUTPUT.PUT_LINE(emp.first_name);
    END LOOP;
END;
/

24. Explicit Cursor Attribute Example with %ROWTYPE

DECLARE
    CURSOR emp_cursor IS
        SELECT employee_id, first_name, salary
        FROM employees;


v_emp emp_cursor%ROWTYPE;


BEGIN
OPEN emp_cursor;


LOOP
    FETCH emp_cursor INTO v_emp;
    EXIT WHEN emp_cursor%NOTFOUND;

    DBMS_OUTPUT.PUT_LINE('ID: ' || v_emp.employee_id);
    DBMS_OUTPUT.PUT_LINE('Name: ' || v_emp.first_name);
    DBMS_OUTPUT.PUT_LINE('Rows fetched: ' || emp_cursor%ROWCOUNT);
END LOOP;

CLOSE emp_cursor;


END;
/

25. Frequently Asked Questions

Question Answer
What are cursor attributes? Properties that provide information about cursor processing.
What are the four main attributes? %FOUND, %NOTFOUND, %ROWCOUNT, %ISOPEN
Which attribute tells whether a row was found? %FOUND
Which attribute tells whether no row was found? %NOTFOUND
Which attribute gives the number of rows processed? %ROWCOUNT
Which attribute tells whether a cursor is open? %ISOPEN
What is the syntax for an implicit cursor attribute? SQL%ROWCOUNT
What is the syntax for an explicit cursor attribute? emp_cursor%ROWCOUNT
Which attribute is commonly used to exit an explicit cursor loop? %NOTFOUND
What does %ROWCOUNT mean for an explicit cursor? The number of rows fetched so far.
What does SQL%ROWCOUNT mean for an implicit cursor? The number of rows affected by the SQL statement.
What is SQL%ISOPEN after an implicit SQL statement completes? FALSE.

Important Exam Points

  1. Cursor attributes provide information about cursor processing.
  2. The four main attributes are %FOUND, %NOTFOUND, %ROWCOUNT, and %ISOPEN.
  3. Implicit cursor syntax uses SQL%attribute.
  4. Explicit cursor syntax uses cursor_name%attribute.
  5. %FOUND means row found or affected.
  6. %NOTFOUND means no row found or affected.
  7. %ROWCOUNT means number of rows processed.
  8. For explicit cursors, %ROWCOUNT increases after successful fetches.
  9. %ISOPEN indicates cursor open status.
  10. SQL%ISOPEN is FALSE after the implicit statement has completed.
  11. %NOTFOUND is commonly used to exit explicit cursor loops.
  12. Cursor attributes relate to the most recent relevant cursor operation.

Quick Revision Table

Attribute Explicit Cursor Implicit Cursor
Found emp_cursor%FOUND SQL%FOUND
Not found emp_cursor%NOTFOUND SQL%NOTFOUND
Row count emp_cursor%ROWCOUNT SQL%ROWCOUNT
Is open emp_cursor%ISOPEN SQL%ISOPEN

One-line memory trick: FOUND = Did it find? NOTFOUND = Did it fail to find? ROWCOUNT = How many? ISOPEN = Is the cursor open?

Oracle Cursor FOR LOOP Faqs With Examples

1. What is a Cursor FOR LOOP?

A Cursor FOR LOOP executes a block of code once for every row returned by a cursor or query.

Basic syntax:

FOR record_name IN cursor_name
LOOP
    statements;
END LOOP;

Example:

DECLARE
    CURSOR emp_cursor IS
        SELECT employee_id, first_name, salary
        FROM employees;
BEGIN
    FOR emp IN emp_cursor
    LOOP
        DBMS_OUTPUT.PUT_LINE(
            emp.employee_id || ' - ' ||
            emp.first_name || ' - ' ||
            emp.salary
        );
    END LOOP;
END;
/

2. Why use a Cursor FOR LOOP?

It simplifies cursor processing. With a traditional explicit cursor, you need DECLARE, OPEN, FETCH, LOOP, EXIT, and CLOSE. With a Cursor FOR LOOP, Oracle automatically handles the cursor lifecycle.

FOR emp IN emp_cursor
LOOP
    ...
END LOOP;

Main advantage: No explicit OPEN, FETCH, or CLOSE is required.

3. What happens internally in a Cursor FOR LOOP?

Conceptually, PL/SQL performs this sequence:

Open cursor
    ↓
Fetch row 1
    ↓
Execute loop body
    ↓
Fetch row 2
    ↓
Execute loop body
    ↓
Fetch row 3
    ↓
Execute loop body
    ↓
No more rows
    ↓
Close cursor

4. Do we need to OPEN the cursor?

No. The Cursor FOR LOOP automatically opens the cursor.

5. Do we need to FETCH the cursor?

No. The Cursor FOR LOOP automatically fetches each row.

6. Do we need to CLOSE the cursor?

No. Oracle automatically closes the cursor after the loop finishes.

7. What is the syntax using a declared cursor?

DECLARE
    CURSOR emp_cursor IS
        SELECT employee_id, first_name
        FROM employees;
BEGIN
    FOR emp IN emp_cursor
    LOOP
        DBMS_OUTPUT.PUT_LINE(emp.first_name);
    END LOOP;
END;
/

Here, emp_cursor is the explicit cursor and emp is the loop record.

8. What is the loop record?

The variable after FOR is called the loop record. You can access the columns using emp.employee_id, emp.first_name, and emp.salary.

9. Do we need to declare the loop record?

No. PL/SQL automatically creates the loop record.

BEGIN
    FOR emp IN (
        SELECT employee_id, first_name, salary
        FROM employees
    )
    LOOP
        DBMS_OUTPUT.PUT_LINE(emp.first_name);
    END LOOP;
END;
/

10. Can we use a query directly in a Cursor FOR LOOP?

Yes. This is called an implicit cursor FOR LOOP.

BEGIN
    FOR emp IN (
        SELECT employee_id, first_name, salary
        FROM employees
        WHERE department_id = 10
    )
    LOOP
        DBMS_OUTPUT.PUT_LINE(
            emp.employee_id || ' - ' ||
            emp.first_name
        );
    END LOOP;
END;
/

11. Named cursor vs Inline query

Named Cursor Inline Query
Cursor declared separately Query written directly
Can be reused Usually used for one loop
More structured Shorter and convenient

12. Can a Cursor FOR LOOP process multiple rows?

Yes. If 100 rows are returned, the loop executes 100 times.

13. What if the query returns no rows?

The loop simply executes zero times. A Cursor FOR LOOP with zero rows does not raise NO_DATA_FOUND.

BEGIN
    FOR emp IN (
        SELECT employee_id, first_name
        FROM employees
        WHERE employee_id = 999999
    )
    LOOP
        DBMS_OUTPUT.PUT_LINE(emp.first_name);
    END LOOP;

    DBMS_OUTPUT.PUT_LINE('Loop completed');
END;
/

14. Can we use WHERE conditions?

Yes.

BEGIN
    FOR emp IN (
        SELECT employee_id, first_name, salary
        FROM employees
        WHERE salary > 50000
    )
    LOOP
        DBMS_OUTPUT.PUT_LINE(
            emp.first_name || ' earns ' ||
            emp.salary
        );
    END LOOP;
END;
/

15. Can we use ORDER BY?

Yes.

BEGIN
    FOR emp IN (
        SELECT employee_id, first_name, salary
        FROM employees
        ORDER BY salary DESC
    )
    LOOP
        DBMS_OUTPUT.PUT_LINE(
            emp.first_name || ' - ' || emp.salary
        );
    END LOOP;
END;
/

16. Can we use joins?

Yes.

BEGIN
    FOR emp IN (
        SELECT e.employee_id,
               e.first_name,
               d.department_name
        FROM employees e
        JOIN departments d
          ON e.department_id = d.department_id
    )
    LOOP
        DBMS_OUTPUT.PUT_LINE(
            emp.first_name || ' - ' ||
            emp.department_name
        );
    END LOOP;
END;
/

17. Can we use aggregate functions?

Yes.

BEGIN
    FOR rec IN (
        SELECT department_id,
               COUNT(*) AS employee_count,
               AVG(salary) AS average_salary
        FROM employees
        GROUP BY department_id
    )
    LOOP
        DBMS_OUTPUT.PUT_LINE(
            'Department: ' || rec.department_id ||
            ', Employees: ' || rec.employee_count ||
            ', Average: ' || rec.average_salary
        );
    END LOOP;
END;
/

18. Can we use IF inside a Cursor FOR LOOP?

Yes.

BEGIN
    FOR emp IN (
        SELECT employee_id, first_name, salary
        FROM employees
    )
    LOOP
        IF emp.salary >= 100000 THEN
            DBMS_OUTPUT.PUT_LINE(
                emp.first_name || ' - High salary'
            );
        ELSE
            DBMS_OUTPUT.PUT_LINE(
                emp.first_name || ' - Regular salary'
            );
        END IF;
    END LOOP;
END;
/

19. Can we use UPDATE inside a Cursor FOR LOOP?

Yes.

BEGIN
    FOR emp IN (
        SELECT employee_id, salary
        FROM employees
        WHERE department_id = 10
    )
    LOOP
        IF emp.salary < 50000 THEN
            UPDATE employees
            SET salary = salary + 5000
            WHERE employee_id = emp.employee_id;
        END IF;
    END LOOP;

```
COMMIT;
```

END;
/

20. Can we use DELETE inside a Cursor FOR LOOP?

Yes.

BEGIN
    FOR emp IN (
        SELECT employee_id
        FROM employees
        WHERE salary < 20000
    )
    LOOP
        DELETE FROM employees
        WHERE employee_id = emp.employee_id;
    END LOOP;

```
COMMIT;
```

END;
/

21. What is FOR UPDATE in a Cursor FOR LOOP?

FOR UPDATE can be used when the selected rows are going to be updated or deleted.

DECLARE
    CURSOR emp_cursor IS
        SELECT employee_id, salary
        FROM employees
        WHERE department_id = 10
        FOR UPDATE;
BEGIN
    FOR emp IN emp_cursor
    LOOP
        IF emp.salary < 50000 THEN
            UPDATE employees
            SET salary = salary + 5000
            WHERE CURRENT OF emp_cursor;
        END IF;
    END LOOP;

    COMMIT;
END;
/

22. What is WHERE CURRENT OF?

WHERE CURRENT OF refers to the current row selected by a FOR UPDATE cursor.

UPDATE employees
SET salary = salary + 1000
WHERE CURRENT OF emp_cursor;

Remember:

FOR UPDATE
     ↓
Cursor identifies/locks rows for update
     ↓
WHERE CURRENT OF
     ↓
Update/delete current row

23. Can a parameterized cursor be used with a Cursor FOR LOOP?

Yes.

DECLARE
    CURSOR emp_cursor(p_dept_id NUMBER) IS
        SELECT employee_id, first_name, salary
        FROM employees
        WHERE department_id = p_dept_id;
BEGIN
    FOR emp IN emp_cursor(10)
    LOOP
        DBMS_OUTPUT.PUT_LINE(
            emp.employee_id || ' - ' ||
            emp.first_name
        );
    END LOOP;
END;
/

24. Can we pass different parameters?

Yes.

DECLARE
    CURSOR emp_cursor(p_dept_id NUMBER) IS
        SELECT employee_id, first_name
        FROM employees
        WHERE department_id = p_dept_id;
BEGIN
    FOR emp IN emp_cursor(10)
    LOOP
        DBMS_OUTPUT.PUT_LINE(emp.first_name);
    END LOOP;

```
FOR emp IN emp_cursor(20)
LOOP
    DBMS_OUTPUT.PUT_LINE(emp.first_name);
END LOOP;
```

END;
/

25. Can we use %ROWCOUNT with a Cursor FOR LOOP?

The loop itself automatically manages fetching, so you generally do not need cursor attributes for basic iteration. If you need a count of processed rows, maintain your own counter.

DECLARE
    v_count NUMBER := 0;
BEGIN
    FOR emp IN (
        SELECT employee_id, first_name
        FROM employees
    )
    LOOP
        v_count := v_count + 1;
        DBMS_OUTPUT.PUT_LINE(emp.first_name);
    END LOOP;

    DBMS_OUTPUT.PUT_LINE(
        'Total rows processed: ' || v_count
    );
END;
/

26. Can we use EXIT inside a Cursor FOR LOOP?

Yes.

BEGIN
    FOR emp IN (
        SELECT employee_id, first_name
        FROM employees
        ORDER BY employee_id
    )
    LOOP
        DBMS_OUTPUT.PUT_LINE(emp.first_name);

```
    EXIT WHEN emp.employee_id = 105;
END LOOP;
```

END;
/

27. Can we use CONTINUE inside a Cursor FOR LOOP?

Yes.

BEGIN
    FOR emp IN (
        SELECT employee_id, first_name, salary
        FROM employees
    )
    LOOP
        CONTINUE WHEN emp.salary < 30000;

```
    DBMS_OUTPUT.PUT_LINE(
        emp.first_name || ' - ' || emp.salary
    );
END LOOP;
```

END;
/

28. EXIT vs CONTINUE

EXIT CONTINUE
Terminates the loop Skips the current iteration
Stops processing completely Moves to the next row

29. Can we use an exception handler?

Yes.

BEGIN
    FOR emp IN (
        SELECT employee_id, first_name
        FROM employees
    )
    LOOP
        DBMS_OUTPUT.PUT_LINE(emp.first_name);
    END LOOP;

EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Error: ' || SQLERRM);
END;
/

30. What happens if an error occurs inside the loop?

Control transfers to the applicable exception handler. For a Cursor FOR LOOP, the cursor is automatically managed by PL/SQL.

31. Do we need to explicitly CLOSE the cursor in an exception?

No. For a Cursor FOR LOOP, no manual close is required.

32. Cursor FOR LOOP vs Traditional Explicit Cursor

Traditional explicit cursor:

DECLARE
    CURSOR emp_cursor IS
        SELECT employee_id, first_name
        FROM employees;

```
v_id   employees.employee_id%TYPE;
v_name employees.first_name%TYPE;
```

BEGIN
OPEN emp_cursor;

```
LOOP
    FETCH emp_cursor INTO v_id, v_name;
    EXIT WHEN emp_cursor%NOTFOUND;
    DBMS_OUTPUT.PUT_LINE(v_name);
END LOOP;

CLOSE emp_cursor;
```

END;
/

Cursor FOR LOOP:

DECLARE
    CURSOR emp_cursor IS
        SELECT employee_id, first_name
        FROM employees;
BEGIN
    FOR emp IN emp_cursor
    LOOP
        DBMS_OUTPUT.PUT_LINE(emp.first_name);
    END LOOP;
END;
/

Key difference: the Cursor FOR LOOP eliminates manual OPEN, FETCH, EXIT WHEN %NOTFOUND, and CLOSE.

33. Cursor FOR LOOP vs Implicit Cursor

Implicit cursor is Oracle automatically managing a SQL statement, while the Cursor FOR LOOP processes rows using automatic cursor handling for iteration.

34. What happens if the query returns 1,000 rows?

The loop body executes once per row, so it runs 1,000 times.

35. Can the loop record be modified?

The loop record should be treated as a read-only representation of the current fetched row. To modify database data, use SQL such as UPDATE or DELETE.

36. Can we use aliases in a Cursor FOR LOOP?

Yes.

BEGIN
    FOR emp IN (
        SELECT employee_id AS id,
               first_name AS name,
               salary AS pay
        FROM employees
    )
    LOOP
        DBMS_OUTPUT.PUT_LINE(
            emp.id || ' - ' ||
            emp.name || ' - ' ||
            emp.pay
        );
    END LOOP;
END;
/

37. Can we use expressions in the query?

Yes.

BEGIN
    FOR emp IN (
        SELECT first_name,
               salary,
               salary * 12 AS annual_salary
        FROM employees
    )
    LOOP
        DBMS_OUTPUT.PUT_LINE(
            emp.first_name ||
            ' Annual Salary: ' ||
            emp.annual_salary
        );
    END LOOP;
END;
/

38. Can we use nested Cursor FOR LOOPS?

Yes.

BEGIN
    FOR dept IN (
        SELECT department_id, department_name
        FROM departments
    )
    LOOP
        DBMS_OUTPUT.PUT_LINE(
            'Department: ' || dept.department_name
        );

```
    FOR emp IN (
        SELECT employee_id, first_name
        FROM employees
        WHERE department_id = dept.department_id
    )
    LOOP
        DBMS_OUTPUT.PUT_LINE(
            '  Employee: ' || emp.first_name
        );
    END LOOP;
END LOOP;
```

END;
/

39. Advantages of Cursor FOR LOOP

  1. Simple syntax
  2. No explicit OPEN
  3. No explicit FETCH
  4. No explicit CLOSE
  5. No need to declare a loop record
  6. Automatically processes multiple rows
  7. Less code
  8. Lower chance of cursor-management mistakes
  9. Works with named cursors and inline queries
  10. Supports parameterized cursors

40. Disadvantages

For many straightforward row-processing tasks, there are not significant disadvantages compared with manually managed explicit cursors. However, a manually controlled explicit cursor may be preferable when you specifically need control over when the cursor is opened, fetched, or closed. For large-scale data manipulation, a set-based SQL statement is often more efficient than row-by-row processing.

41. Frequently Asked Questions

Question Answer
What is a Cursor FOR LOOP? A PL/SQL loop that processes each row returned by a cursor or query.
Does it automatically open the cursor? Yes.
Does it automatically fetch rows? Yes.
Does it automatically close the cursor? Yes.
Do we need EXIT WHEN cursor%NOTFOUND? No.
Do we need to declare a loop variable? No.
Can we use an inline SELECT? Yes.
What happens if the query returns zero rows? The loop body executes zero times.
Can a Cursor FOR LOOP process multiple rows? Yes.
Can we use parameters? Yes, with a parameterized cursor.

Important Exam Points

  1. Cursor FOR LOOP processes rows one at a time.
  2. It automatically performs OPEN → FETCH → CLOSE.
  3. No explicit OPEN is required.
  4. No explicit FETCH is required.
  5. No explicit CLOSE is required.
  6. No EXIT WHEN %NOTFOUND is required.
  7. The loop variable is automatically created as a record.
  8. It can use a named cursor.
  9. It can use an inline query.
  10. Zero rows → loop body executes zero times.
  11. Multiple rows → loop body executes once per row.
  12. FOR UPDATE and WHERE CURRENT OF can be used for appropriate update scenarios.
  13. EXIT terminates the loop.
  14. CONTINUE skips the current iteration.
  15. Parameterized cursors can be used with Cursor FOR LOOP.
  16. For large set-based changes, prefer a single SQL statement when possible.

Quick Revision

CURSOR FOR LOOP
        |
  +-----+-----+
  |           |
Named     Inline Query
Cursor    (SELECT ...)
  |           |
  +-----+-----+
        |
    Automatic
        |
OPEN → FETCH → CLOSE
        |
   Process each row

One-line memory trick: Cursor FOR LOOP = process every returned row without manually OPENing, FETCHing, or CLOSEing the cursor.