Implicit Cursors
Whenever Oracle executes a SQL statement inside PL/SQL, it automatically creates and manages a cursor — a private work area in memory. For DML statements and SELECT INTO, Oracle manages this cursor invisibly: this is the implicit cursor.
What Is an Implicit Cursor?
You do not OPEN, FETCH, or CLOSE an implicit cursor. Oracle does all that for you. After any SQL statement executes, Oracle populates a set of cursor attributes on the special SQL cursor that reflect the outcome of the most recent statement.
Implicit Cursor Attributes
| Attribute | Type | Meaning |
|---|---|---|
SQL%FOUND |
BOOLEAN | TRUE if the last statement affected/returned at least one row |
SQL%NOTFOUND |
BOOLEAN | TRUE if the last statement affected/returned no rows |
SQL%ROWCOUNT |
NUMBER | Number of rows affected by the last DML or SELECT INTO |
SQL%ISOPEN |
BOOLEAN | Always FALSE for implicit cursors (Oracle closes them automatically) |
SQL%ISOPEN is always FALSE for implicit cursors. It is only meaningful for explicit cursors, which you open and close manually.
After SELECT INTO
DECLARE
v_name employees.first_name%TYPE;
BEGIN
SELECT first_name
INTO v_name
FROM employees
WHERE employee_id = 100;
-- SQL%FOUND is TRUE because exactly one row was returned
IF SQL%FOUND THEN
DBMS_OUTPUT.PUT_LINE('Found: ' || v_name);
END IF;
-- SQL%ROWCOUNT is 1
DBMS_OUTPUT.PUT_LINE('Rows fetched: ' || SQL%ROWCOUNT);
EXCEPTION
WHEN NO_DATA_FOUND THEN
-- SQL%FOUND would be FALSE here if we checked before the exception
DBMS_OUTPUT.PUT_LINE('No employee found.');
END;
/
After UPDATE
DECLARE
v_dept_id employees.department_id%TYPE := 60;
BEGIN
UPDATE employees
SET salary = salary * 1.05
WHERE department_id = v_dept_id;
IF SQL%FOUND THEN
DBMS_OUTPUT.PUT_LINE(
SQL%ROWCOUNT || ' employees in dept ' || v_dept_id || ' got a 5% raise.'
);
ELSE
DBMS_OUTPUT.PUT_LINE('No employees found in dept ' || v_dept_id || '.');
END IF;
COMMIT;
END;
/
Output (if dept 60 has 5 employees):
5 employees in dept 60 got a 5% raise.
After DELETE
DECLARE
v_cutoff_date DATE := ADD_MONTHS(SYSDATE, -60); -- 5 years ago
BEGIN
DELETE FROM job_history
WHERE end_date < v_cutoff_date;
DBMS_OUTPUT.PUT_LINE('Deleted: ' || SQL%ROWCOUNT || ' old job history rows.');
IF SQL%NOTFOUND THEN
DBMS_OUTPUT.PUT_LINE('Nothing to delete.');
END IF;
COMMIT;
END;
/
After INSERT
BEGIN
INSERT INTO departments (department_id, department_name, location_id)
VALUES (departments_seq.NEXTVAL, 'Data Engineering', 1700);
-- SQL%ROWCOUNT is 1 after a single-row INSERT
DBMS_OUTPUT.PUT_LINE('Inserted ' || SQL%ROWCOUNT || ' department.');
COMMIT;
END;
/
Combining SQL% Checks in a Procedure
CREATE OR REPLACE PROCEDURE transfer_employee(
p_emp_id IN employees.employee_id%TYPE,
p_new_dept_id IN employees.department_id%TYPE
) IS
BEGIN
UPDATE employees
SET department_id = p_new_dept_id,
manager_id = (SELECT manager_id
FROM departments
WHERE department_id = p_new_dept_id)
WHERE employee_id = p_emp_id;
IF SQL%NOTFOUND THEN
RAISE_APPLICATION_ERROR(-20001,
'Employee ' || p_emp_id || ' does not exist.');
END IF;
DBMS_OUTPUT.PUT_LINE(
'Employee ' || p_emp_id ||
' transferred to department ' || p_new_dept_id ||
'. Rows updated: ' || SQL%ROWCOUNT
);
COMMIT;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
RAISE;
END transfer_employee;
/
-- Test
BEGIN
transfer_employee(107, 90);
END;
/
SQL%ROWCOUNT with Bulk Operations
DECLARE
v_dept_id NUMBER := 80;
BEGIN
-- Give all Sales employees a holiday bonus
UPDATE employees
SET salary = salary + 500
WHERE department_id = v_dept_id;
DBMS_OUTPUT.PUT_LINE('Bonus applied to ' || SQL%ROWCOUNT || ' Sales reps.');
COMMIT;
END;
/
SQL%ROWCOUNT and SQL%FOUND reflect the most recent SQL statement. If you need to check attributes, do so immediately after the DML — any subsequent SQL statement resets them.
Key Difference: Implicit vs Explicit Cursors
| Feature | Implicit | Explicit |
|---|---|---|
| Declared by developer | No | Yes |
| OPEN / FETCH / CLOSE | Oracle handles it | Developer calls each |
| Used for | Single-row SELECT, DML | Multi-row queries |
| Attributes | SQL%FOUND, SQL%ROWCOUNT, etc. |
cursor_name%FOUND, etc. |
Summary
- Implicit cursors are created automatically for every SQL statement inside PL/SQL.
SQL%FOUND/SQL%NOTFOUNDindicate whether rows were affected.SQL%ROWCOUNTtells you how many rows the last DML orSELECT INTOtouched.SQL%ISOPENis alwaysFALSEfor implicit cursors.- Check cursor attributes immediately after the SQL — a subsequent statement resets them.
- For multi-row results, use explicit cursors or cursor FOR loops (next topic).