CREATE OR REPLACE PACKAGE tmr
IS
PROCEDURE start_timer;
PROCEDURE show_elapsed (str IN VARCHAR2);
END tmr;
Package created.
CREATE OR REPLACE PACKAGE BODY tmr
IS
last_timing NUMBER := NULL;
PROCEDURE start_timer
IS
BEGIN
last_timing := DBMS_UTILITY.get_time;
END;
PROCEDURE show_elapsed (str IN VARCHAR2)
IS
BEGIN
DBMS_OUTPUT.put_line (
str
|| ': '
|| MOD (DBMS_UTILITY.get_time - last_timing + POWER (2, 32),
POWER (2, 32)));
start_timer;
END;
END tmr;
Package Body created.
CREATE TABLE not_much_stuff (n NUMBER)
Table created.
INSERT INTO not_much_stuff
SELECT LEVEL
FROM DUAL
CONNECT BY LEVEL < 11
10 row(s) inserted.
Demonstration of Exception Behavior with SELECT-INTO
DECLARE
my_n not_much_stuff.n%TYPE;
BEGIN
DBMS_OUTPUT.put_line ('No rows found:');
BEGIN
SELECT n
INTO my_n
FROM not_much_stuff
WHERE n = -1;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
DBMS_OUTPUT.put_line (DBMS_UTILITY.format_error_stack);
END;
DBMS_OUTPUT.put_line ('Too many rows found:');
BEGIN
SELECT n
INTO my_n
FROM not_much_stuff
WHERE n BETWEEN 1 AND 10;
EXCEPTION
WHEN TOO_MANY_ROWS
THEN
DBMS_OUTPUT.put_line (DBMS_UTILITY.format_error_stack);
END;
END;
No rows found:
ORA-01403: no data found
Too many rows found:
ORA-01422: exact fetch returns more than requested number of rows
DECLARE
my_n not_much_stuff.n%TYPE;
BEGIN
tmr.start_timer;
FOR indx IN 1 .. 10000
LOOP
BEGIN
SELECT n
INTO my_n
FROM not_much_stuff
WHERE n = -1;
my_n := 100;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
my_n := 100;
END;
END LOOP;
DBMS_OUTPUT.put_line (my_n);
tmr.show_elapsed ('Implicit Failure');
END;
100
Implicit Failure: 56
DECLARE
my_n not_much_stuff.n%TYPE;
BEGIN
tmr.start_timer;
FOR indx IN 1 .. 10000
LOOP
BEGIN
SELECT n
INTO my_n
FROM not_much_stuff
WHERE n = 1;
my_n := 100;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
my_n := 100;
END;
END LOOP;
DBMS_OUTPUT.put_line (my_n);
tmr.show_elapsed ('Implicit Success');
END;
100
Implicit Success: 34
DECLARE
my_n not_much_stuff.n%TYPE;
CURSOR stuff_cur
IS
SELECT n
FROM not_much_stuff
WHERE n = -1;
BEGIN
tmr.start_timer;
FOR indx IN 1 .. 10000
LOOP
OPEN stuff_cur;
FETCH stuff_cur INTO my_n;
IF stuff_cur%NOTFOUND
THEN
my_n := 100;
END IF;
CLOSE stuff_cur;
END LOOP;
DBMS_OUTPUT.put_line (my_n);
tmr.show_elapsed ('Explicit Failure');
END;
100
Explicit Failure: 46
DECLARE
my_n not_much_stuff.n%TYPE;
CURSOR stuff_cur
IS
SELECT n
FROM not_much_stuff
WHERE n = 1;
BEGIN
tmr.start_timer;
FOR indx IN 1 .. 10000
LOOP
OPEN stuff_cur;
FETCH stuff_cur INTO my_n;
IF stuff_cur%FOUND
THEN
my_n := 100;
END IF;
CLOSE stuff_cur;
END LOOP;
DBMS_OUTPUT.put_line (my_n);
tmr.show_elapsed ('Explicit Success');
END;
100
Explicit Success: 33
DECLARE
my_n not_much_stuff.n%TYPE;
BEGIN
tmr.start_timer;
FOR indx IN 1 .. 10000
LOOP
FOR rec IN (SELECT n
FROM not_much_stuff
WHERE n = -1)
LOOP
my_n := rec.n;
END LOOP;
END LOOP;
DBMS_OUTPUT.put_line (my_n);
tmr.show_elapsed ('CFL Failure');
END;
CFL Failure: 33
DECLARE
my_n not_much_stuff.n%TYPE;
BEGIN
tmr.start_timer;
FOR indx IN 1 .. 10000
LOOP
FOR rec IN (SELECT n
FROM not_much_stuff
WHERE n = 1)
LOOP
my_n := rec.n;
END LOOP;
END LOOP;
DBMS_OUTPUT.put_line (my_n);
tmr.show_elapsed ('CFL Success');
END;
1
CFL Success: 38
1. Implicit cursor inside a nested block
CREATE OR REPLACE PROCEDURE do_stuff_with_employee (
employee_id_in IN hr.employees.employee_id%TYPE)
IS
l_name hr.employees.last_name%TYPE;
BEGIN
BEGIN
SELECT last_name
INTO l_name
FROM hr.employees e
WHERE e.employee_id = do_stuff_with_employee.employee_id_in;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
/* log the error if this really is an error or let it go... */
l_name := NULL;
END;
IF l_name IS NOT NULL
THEN
/* continue with application logic */
NULL;
END IF;
END;
Procedure created.
2. Implicit cursor inside a nested subprogram
CREATE OR REPLACE PROCEDURE do_stuff_with_employee (
employee_id_in IN hr.employees.employee_id%TYPE)
IS
l_name hr.employees.last_name%TYPE;
FUNCTION emp_name (employee_id_in IN hr.employees.employee_id%TYPE)
RETURN hr.employees.last_name%TYPE
IS
l_name hr.employees.last_name%TYPE;
BEGIN
SELECT last_name
INTO l_name
FROM hr.employees
WHERE employee_id = employee_id_in;
RETURN l_name;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
/* log the error if this really is an error or let it go... */
RETURN NULL;
END;
BEGIN
l_name := emp_name (employee_id_in);
IF l_name IS NOT NULL
THEN
/* continue with application logic */
NULL;
END IF;
END;
Procedure created.
3. Explicit cursor unconcerned with too many rows
CREATE OR REPLACE PROCEDURE do_stuff_with_employee (
employee_id_in IN hr.employees.employee_id%TYPE)
IS
l_name hr.employees.last_name%TYPE;
CURSOR name_cur
IS
SELECT last_name
FROM hr.employees e
WHERE e.employee_id = do_stuff_with_employee.employee_id_in;
BEGIN
OPEN name_cur;
FETCH name_cur INTO l_name;
CLOSE name_cur;
IF l_name IS NOT NULL
THEN
/* continue with application logic */
NULL;
END IF;
END;
Procedure created.
4. Explicit cursor that checks for too many rows
CREATE OR REPLACE PROCEDURE do_stuff_with_employee (
employee_id_in IN hr.employees.employee_id%TYPE)
IS
l_name hr.employees.last_name%TYPE;
l_name2 hr.employees.last_name%TYPE;
CURSOR name_cur
IS
SELECT last_name
FROM hr.employees e
WHERE e.employee_id = do_stuff_with_employee.employee_id_in;
BEGIN
OPEN name_cur;
FETCH name_cur INTO l_name;
FETCH name_cur INTO l_name2;
IF name_cur%FOUND
THEN
CLOSE name_cur;
RAISE TOO_MANY_ROWS;
ELSE
CLOSE name_cur;
END IF;
IF l_name IS NOT NULL
THEN
/* continue with application logic */
NULL;
END IF;
END;
Procedure created.