#=======================================================================
# Writer
: Lokanadham
Thandlam
#=======================================================================
--Example
12–1 Specifying that Subprogram Is To Be Inlined
PROCEDURE p1 (x PLS_INTEGER) IS ...
...
PRAGMA INLINE (p1, 'YES');
x:= p1(1) + p1(2) + 17; -- These 2
invocations to p1 are inlined
...
x:= p1(3) + p1(4) + 17; -- These 2
invocations to p1 are not inlined
...
--Example
12–2 Specifying that Overloaded Subprogram Is To Be Inlined
FUNCTION p2 (p boolean) return PLS_INTEGER IS ...
FUNCTION p2 (x PLS_INTEGER) return PLS_INTEGER IS ...
...
PRAGMA INLINE(p2, 'YES');
x := p2(true) + p2(3);
...
--Example
12–3 Specifying that Subprogram Is Not To Be Inlined
PROCEDURE p1 (x PLS_INTEGER) IS ...
...
PRAGMA INLINE (p1, 'NO');
x:= p1(1) + p1(2) + 17; -- These 2
invocations to p1 are not inlined
...
x:= p1(3) + p1(4) + 17; -- These 2
invocations to p1 might be inlined
...
--Example
12–4 PRAGMA INLINE ... 'NO' Overrides PRAGMA INLINE ... 'YES'
PROCEDURE p1 (x PLS_INTEGER) IS ...
...
PRAGMA INLINE (p1, 'YES');
PRAGMA INLINE (p1, 'NO');
PRAGMA INLINE (p1, 'YES');
x:= p1(1) + p1(2) + 17; -- These 2
invocations to p1 are not inlined
...
--Example
12–5 Nested Query Improves Performance
DECLARE
starting_time TIMESTAMP WITH TIME ZONE;
ending_time TIMESTAMP WITH TIME ZONE;
BEGIN
-- Invokes
SQRT for every row of employees table:
SELECT SYSTIMESTAMP INTO
starting_time FROM DUAL;
FOR item IN (
SELECT DISTINCT(SQRT(department_id)) col_alias
FROM employees
ORDER BY col_alias
)
LOOP
DBMS_OUTPUT.PUT_LINE('Square root
of dept. ID = ' || item.col_alias);
END LOOP;
SELECT SYSTIMESTAMP INTO ending_time
FROM DUAL;
DBMS_OUTPUT.PUT_LINE('Time = ' || TO_CHAR(ending_time -
starting_time));
-- Invokes
SQRT for every distinct department_id of employees table:
SELECT SYSTIMESTAMP INTO
starting_time FROM DUAL;
FOR item IN (
SELECT SQRT(department_id) col_alias
FROM (SELECT DISTINCT
department_id FROM employees)
ORDER BY col_alias
)
LOOP
IF item.col_alias IS NOT NULL THEN
DBMS_OUTPUT.PUT_LINE('Square root
of dept. ID = ' || item.col_alias);
END IF;
END LOOP;
SELECT SYSTIMESTAMP INTO ending_time
FROM DUAL;
DBMS_OUTPUT.PUT_LINE('Time = ' || TO_CHAR(ending_time -
starting_time));
END;
/
--Example
12–6 NOCOPY Subprogram PARAMETERS
DECLARE
TYPE EmpTabTyp IS TABLE OF employees%ROWTYPE;
emp_tab
EmpTabTyp := EmpTabTyp (NULL); --
initialize
t1
NUMBER;
t2
NUMBER;
t3
NUMBER;
PROCEDURE get_time (t OUT NUMBER)
IS
BEGIN
t := DBMS_UTILITY.get_time;
END;
PROCEDURE do_nothing1
(tab IN OUT EmpTabTyp)
IS
BEGIN
NULL;
END;
PROCEDURE do_nothing2
(tab IN OUT NOCOPY EmpTabTyp)
IS
BEGIN
NULL;
END;
BEGIN
SELECT *
INTO emp_tab (1)
FROM employees
WHERE employee_id
= 100;
emp_tab.EXTEND (49999, 1); -- Copy
element 1 into 2..50000
get_time (t1);
do_nothing1 (emp_tab); -- Pass IN
OUT parameter
get_time (t2);
do_nothing2 (emp_tab); -- Pass IN
OUT NOCOPY parameter
get_time (t3);
DBMS_OUTPUT.PUT_LINE ('Call Duration
(secs)');
DBMS_OUTPUT.PUT_LINE ('--------------------');
DBMS_OUTPUT.PUT_LINE ('Just IN OUT:
' || TO_CHAR ( (t2 - t1) / 100.0));
DBMS_OUTPUT.PUT_LINE ('With NOCOPY:
' || TO_CHAR ( (t3 - t2)) / 100.0);
END;
/
--consider
the FOR LOOP statement in Example 12–7. It sends these DML statements from
PL/SQL to SQL one at a time:
DELETE FROM
employees_temp
WHERE
department_id = depts (10);
DELETE FROM
employees_temp
WHERE
department_id = depts (30);
DELETE FROM
employees_temp
WHERE
department_id = depts (70);
--Example
12–7 DELETE Statement in FOR LOOP Statement
DROP TABLE
employees_temp;
CREATE TABLE
employees_temp
AS
SELECT * FROM employees;
DECLARE
TYPE NumList IS VARRAY (20) OF NUMBER;
depts
NumList := NumList (10, 30, 70); --
department numbers
BEGIN
FOR i IN depts.FIRST .. depts.LAST
LOOP
DELETE FROM
employees_temp
WHERE
department_id = depts (i);
END LOOP;
END;
/
--Now
consider the FORALL statement in Example 12–8. It sends the same three DML
statements from PL/SQL to SQL as a batch.
--Example
12–8 DELETE Statement in FORALL Statement
DROP TABLE employees_temp;
CREATE TABLE
employees_temp
AS
SELECT * FROM employees;
DECLARE
TYPE NumList IS VARRAY (20) OF NUMBER;
depts
NumList := NumList (10, 30, 70); --
department numbers
BEGIN
FORALL i IN depts.FIRST .. depts.LAST
DELETE FROM
employees_temp
WHERE
department_id = depts (i);
END;
/
--Example
12–9 Time Difference for INSERT Statement in FOR LOOP and FORALL Statements
DROP TABLE parts1;
CREATE TABLE parts1
(
pnum
INTEGER,
pname
VARCHAR2 (15)
);
DROP TABLE parts2;
CREATE TABLE parts2
(
pnum
INTEGER,
pname
VARCHAR2 (15)
);
DECLARE
TYPE NumTab IS TABLE OF parts1.pnum%TYPE
INDEX BY PLS_INTEGER;
TYPE NameTab IS TABLE OF parts1.pname%TYPE
INDEX BY PLS_INTEGER;
pnums NumTab;
pnames NameTab;
iterations
CONSTANT PLS_INTEGER := 50000;
t1 INTEGER;
t2 INTEGER;
t3 INTEGER;
BEGIN
FOR j IN 1 .. iterations
LOOP
-- populate collections
pnums (j) := j;
pnames (j) := 'Part No. ' || TO_CHAR (j);
END LOOP;
t1 := DBMS_UTILITY.get_time;
FOR i IN 1 .. iterations
LOOP
INSERT INTO parts1 (pnum, pname)
VALUES (pnums (i), pnames (i));
END LOOP;
t2 := DBMS_UTILITY.get_time;
FORALL i IN 1 .. iterations
INSERT INTO parts2 (pnum, pname)
VALUES (pnums (i), pnames (i));
t3 := DBMS_UTILITY.get_time;
DBMS_OUTPUT.PUT_LINE ('Execution
Time (secs)');
DBMS_OUTPUT.PUT_LINE ('---------------------');
DBMS_OUTPUT.PUT_LINE ('FOR LOOP: ' || TO_CHAR ( (t2 - t1) / 100));
DBMS_OUTPUT.PUT_LINE ('FORALL: ' || TO_CHAR ( (t3 - t2) / 100));
COMMIT;
END;
/
--Example
12–10 FORALL Statement for Subset of Collection
DROP TABLE
employees_temp;
CREATE TABLE
employees_temp
AS
SELECT * FROM employees;
DECLARE
TYPE NumList IS VARRAY (10) OF NUMBER;
depts
NumList
:= NumList (5,
10,
20,
30,
50,
55,
57,
60,
70,
75);
BEGIN
FORALL j IN 4 .. 7
DELETE FROM
employees_temp
WHERE
department_id = depts (j);
END;
/
--Example
12–11 FORALL Statements for Sparse Collection and Its Subsets
DROP TABLE
valid_orders;
CREATE TABLE
valid_orders
(
cust_name
VARCHAR2 (32),
amount
NUMBER (10, 2)
);
DROP TABLE big_orders;
CREATE TABLE big_orders
AS
SELECT *
FROM
valid_orders
WHERE 1 = 0;
DROP TABLE
rejected_orders;
CREATE TABLE
rejected_orders
AS
SELECT *
FROM
valid_orders
WHERE 1 = 0;
DECLARE
SUBTYPE cust_name IS
valid_orders.cust_name%TYPE;
TYPE cust_typ IS TABLE OF cust_name;
cust_tab cust_typ; -- Collection of customer names
SUBTYPE
order_amount IS valid_orders.amount%TYPE;
TYPE amount_typ IS TABLE OF NUMBER;
amount_tab amount_typ; -- Collection of order
amounts
TYPE
index_pointer_t IS TABLE OF PLS_INTEGER;
/* Collections for pointers
to elements of cust_tab collection
(to represent two subsets of cust_tab): */
big_order_tab index_pointer_t :=
index_pointer_t ();
rejected_order_tab index_pointer_t :=
index_pointer_t ();
PROCEDURE populate_data_collections
IS
BEGIN
cust_tab :=
cust_typ ('Company1',
'Company2',
'Company3',
'Company4',
'Company5');
amount_tab :=
amount_typ (5000.01,
0,
150.25,
4000.00,
NULL);
END;
BEGIN
populate_data_collections;
DBMS_OUTPUT.PUT_LINE ('--- Original
order data ---');
FOR i IN 1 .. cust_tab.LAST
LOOP
DBMS_OUTPUT.PUT_LINE (
'Customer #' || i || ', ' || cust_tab
(i) || ': $' || amount_tab (i));
END LOOP;
-- Delete invalid orders:
FOR i IN 1 .. cust_tab.LAST
LOOP
IF amount_tab (i) IS NULL OR amount_tab (i) = 0
THEN
cust_tab.delete (i);
amount_tab.delete (i);
END IF;
END LOOP;
-- cust_tab is now a sparse
collection.
DBMS_OUTPUT.PUT_LINE ('--- Order
data with invalid orders deleted ---');
FOR i IN 1 .. cust_tab.LAST
LOOP
IF cust_tab.EXISTS (i)
THEN
DBMS_OUTPUT.PUT_LINE (
'Customer #'
|| i
|| ', '
|| cust_tab (i)
|| ': $'
|| amount_tab (i));
END IF;
END LOOP;
-- Using sparse collection,
populate valid_orders table:
FORALL i IN INDICES OF cust_tab
INSERT INTO
valid_orders (cust_name, amount)
VALUES (cust_tab (i), amount_tab (i));
populate_data_collections; -- Restore
original order data
-- cust_tab is a dense
collection again.
/* Populate collections of
pointers to elements of cust_tab collection
(which represent two subsets of cust_tab):
*/
FOR i IN cust_tab.FIRST .. cust_tab.LAST
LOOP
IF amount_tab (i) IS NULL OR amount_tab (i) = 0
THEN
rejected_order_tab.EXTEND;
rejected_order_tab (rejected_order_tab.LAST) := i;
END IF;
IF amount_tab (i) > 2000
THEN
big_order_tab.EXTEND;
big_order_tab (big_order_tab.LAST) := i;
END IF;
END LOOP;
/* Using each subset in a
different FORALL statement,
populate rejected_orders and big_orders
tables: */
FORALL i IN VALUES OF
rejected_order_tab
INSERT INTO
rejected_orders (cust_name, amount)
VALUES (cust_tab (i), amount_tab (i));
FORALL i IN VALUES OF
big_order_tab
INSERT INTO big_orders (cust_name, amount)
VALUES (cust_tab (i), amount_tab (i));
END;
/
--Verify
that correct order details were stored:
SELECT cust_name "Customer", amount "Valid
order amount"
FROM
valid_orders
ORDER BY cust_name;
SELECT cust_name "Customer", amount "Big
order amount"
FROM big_orders
ORDER BY cust_name;
SELECT cust_name "Customer", amount "Rejected
order amount"
FROM
rejected_orders
ORDER BY cust_name;
--Example
12–12 Handling FORALL EXCEPTIONS Immediately
DROP TABLE emp_temp;
CREATE TABLE emp_temp
(
deptno
NUMBER (2),
job VARCHAR2 (18)
);
CREATE OR REPLACE PROCEDURE p
AUTHID DEFINER
AS
TYPE NumList IS TABLE OF NUMBER;
depts NumList := NumList (10, 20, 30);
error_message VARCHAR2 (100);
BEGIN
-- Populate table:
INSERT INTO emp_temp (deptno, job)
VALUES (10, 'Clerk');
INSERT INTO emp_temp (deptno, job)
VALUES (20, 'Bookkeeper');
INSERT INTO emp_temp (deptno, job)
VALUES (30, 'Analyst');
COMMIT;
-- Append 9-character string
to each job:
FORALL j IN depts.FIRST .. depts.LAST
UPDATE emp_temp
SET job = job || ' (Senior)'
WHERE deptno = depts (j);
EXCEPTION
WHEN OTHERS
THEN
error_message := SQLERRM;
DBMS_OUTPUT.PUT_LINE (error_message);
COMMIT; -- Commit
results of successful updates
RAISE;
END;
/
--Invoke
procedure:
BEGIN
p;
END;
/
--Example
12–13 Handling FORALL Exceptions After FORALL Statement Completes
CREATE OR REPLACE PROCEDURE p
AUTHID DEFINER
AS
TYPE NumList IS TABLE OF NUMBER;
depts NumList := NumList (10, 20, 30);
error_message VARCHAR2 (100);
bad_stmt_no PLS_INTEGER;
bad_deptno emp_temp.deptno%TYPE;
bad_job emp_temp.job%TYPE;
dml_errors EXCEPTION;
PRAGMA EXCEPTION_INIT (dml_errors, -24381);
BEGIN
-- Populate table:
INSERT INTO emp_temp (deptno, job)
VALUES (10, 'Clerk');
INSERT INTO emp_temp (deptno, job)
VALUES (20, 'Bookkeeper');
INSERT INTO emp_temp (deptno, job)
VALUES (30, 'Analyst');
COMMIT;
-- Append 9-character string
to each job:
FORALL j IN depts.FIRST .. depts.LAST SAVE EXCEPTIONS
UPDATE emp_temp
SET job = job || ' (Senior)'
WHERE deptno = depts (j);
EXCEPTION
WHEN dml_errors
THEN
FOR i IN 1 .. SQL%BULK_EXCEPTIONS.COUNT
LOOP
error_message := SQLERRM (- (SQL%BULK_EXCEPTIONS
(i).ERROR_CODE));
DBMS_OUTPUT.PUT_LINE (error_message);
bad_stmt_no := SQL%BULK_EXCEPTIONS
(i).ERROR_INDEX;
DBMS_OUTPUT.PUT_LINE ('Bad statement
#: ' || bad_stmt_no);
bad_deptno := depts (bad_stmt_no);
DBMS_OUTPUT.PUT_LINE ('Bad
department #: ' || bad_deptno);
SELECT job
INTO bad_job
FROM emp_temp
WHERE deptno = bad_deptno;
DBMS_OUTPUT.PUT_LINE ('Bad job: ' || bad_job);
END LOOP;
COMMIT; -- Commit
results of successful updates
WHEN OTHERS
THEN
DBMS_OUTPUT.PUT_LINE ('Unrecognized
error.');
RAISE;
END;
/
--Invoke
procedure:
BEGIN
p;
END;
/
SELECT * FROM emp_temp;
--Example
12–14 Showing Number of Rows Affected by Each DELETE in FORALL
DROP TABLE emp_temp;
CREATE TABLE emp_temp
AS
SELECT * FROM employees;
DECLARE
TYPE NumList IS TABLE OF NUMBER;
depts
NumList := NumList (30, 50, 60);
BEGIN
FORALL j IN depts.FIRST .. depts.LAST
DELETE FROM emp_temp
WHERE
department_id = depts (j);
FOR i IN depts.FIRST .. depts.LAST
LOOP
DBMS_OUTPUT.PUT_LINE (
'Statement #'
|| i
|| ' deleted '
|| SQL%BULK_ROWCOUNT
(i)
|| ' rows.');
END LOOP;
DBMS_OUTPUT.PUT_LINE ('Total rows
deleted: ' || SQL%ROWCOUNT);
END;
/
--Example
12–15 Showing Number of Rows Affected by Each INSERT SELECT in FORALL
DROP TABLE emp_by_dept;
CREATE TABLE emp_by_dept
AS
SELECT employee_id,
department_id
FROM employees
WHERE 1 = 0;
DECLARE
TYPE dept_tab IS TABLE OF departments.department_id%TYPE;
deptnums
dept_tab;
BEGIN
SELECT
department_id
BULK COLLECT INTO deptnums
FROM departments;
FORALL i IN 1 .. deptnums.COUNT
INSERT INTO emp_by_dept
(employee_id, department_id)
SELECT employee_id,
department_id
FROM employees
WHERE
department_id = deptnums (i)
ORDER BY
department_id, employee_id;
FOR i IN 1 .. deptnums.COUNT
LOOP
-- Count how many rows were
inserted for each department; that is,
-- how many employees are in
each department.
DBMS_OUTPUT.PUT_LINE (
'Dept '
|| deptnums (i)
|| ': inserted '
|| SQL%BULK_ROWCOUNT
(i)
|| ' records');
END LOOP;
DBMS_OUTPUT.PUT_LINE ('Total records
inserted: ' || SQL%ROWCOUNT);
END;
/
--Example
12–16 BULK-Selecting Two DATABASE COLUMNS INTO Two NESTED TABLES
DECLARE
TYPE NumTab IS TABLE OF employees.employee_id%TYPE;
TYPE NameTab IS TABLE OF employees.last_name%TYPE;
enums
NumTab;
names
NameTab;
PROCEDURE
print_first_n (n POSITIVE)
IS
BEGIN
IF enums.COUNT = 0
THEN
DBMS_OUTPUT.PUT_LINE ('Collections
are empty.');
ELSE
DBMS_OUTPUT.PUT_LINE ('First ' || n || ' employees:');
FOR i IN 1 .. n
LOOP
DBMS_OUTPUT.PUT_LINE (
' Employee #' || enums (i) || ': ' || names (i));
END LOOP;
END IF;
END;
BEGIN
SELECT employee_id, last_name
BULK COLLECT INTO enums, names
FROM employees
ORDER BY employee_id;
print_first_n (3);
print_first_n (6);
END;
/
--Example
12–17 BULK-Selecting INTO NESTED TABLE OF RECORDS
DECLARE
CURSOR c1
IS
SELECT first_name, last_name, hire_date FROM employees;
TYPE NameSet IS TABLE OF c1%ROWTYPE;
stock_managers NameSet; -- nested
table of records
BEGIN
-- Assign values to nested
table of records:
SELECT first_name, last_name, hire_date
BULK COLLECT INTO
stock_managers
FROM employees
WHERE job_id = 'ST_MAN'
ORDER BY hire_date;
-- Print nested table of
records:
FOR i IN
stock_managers.FIRST .. stock_managers.LAST
LOOP
DBMS_OUTPUT.PUT_LINE (
stock_managers (i).hire_date
|| ' '
|| stock_managers (i).last_name
|| ', '
|| stock_managers (i).first_name);
END LOOP;
END;
/
--Example
12–18 SELECT BULK COLLECT INTO Statement with Unexpected Results
CREATE OR REPLACE TYPE
numbers_type AUTHID DEFINER IS TABLE OF INTEGER
/
CREATE OR REPLACE PROCEDURE p (i IN INTEGER)
AUTHID DEFINER
IS
numbers1
numbers_type
:=
numbers_type (1,
2,
3,
4,
5);
BEGIN
DBMS_OUTPUT.PUT_LINE ('Before SELECT
statement');
DBMS_OUTPUT.PUT_LINE ('numbers1.COUNT()
= ' || numbers1.COUNT ());
FOR j IN 1 .. numbers1.COUNT ()
LOOP
DBMS_OUTPUT.PUT_LINE ('numbers1(' || j || ') = ' || numbers1
(j));
END LOOP;
--Self-selecting BULK COLLECT
INTO clause:
SELECT a.COLUMN_VALUE
BULK COLLECT INTO numbers1
FROM TABLE (numbers1) a
WHERE a.COLUMN_VALUE > p.i
ORDER BY a.COLUMN_VALUE;
DBMS_OUTPUT.PUT_LINE ('After SELECT
statement');
DBMS_OUTPUT.PUT_LINE ('numbers1.COUNT()
= ' || numbers1.COUNT ());
END p;
/
--Invoke p:
BEGIN
p (2);
END;
/
--Invoke p:
BEGIN
p (10);
END;
/
--Example
12–19 CURSOR Workaround FOR Example 12–18
CREATE OR REPLACE TYPE
numbers_type IS TABLE OF INTEGER
/
CREATE OR REPLACE PROCEDURE p (i IN INTEGER)
AUTHID DEFINER
IS
numbers1
numbers_type
:=
numbers_type (1,
2,
3,
4,
5);
CURSOR c
IS
SELECT a.COLUMN_VALUE
FROM TABLE (numbers1) a
WHERE a.COLUMN_VALUE > p.i
ORDER BY a.COLUMN_VALUE;
BEGIN
DBMS_OUTPUT.PUT_LINE ('Before FETCH
statement');
DBMS_OUTPUT.PUT_LINE ('numbers1.COUNT()
= ' || numbers1.COUNT ());
FOR j IN 1 .. numbers1.COUNT ()
LOOP
DBMS_OUTPUT.PUT_LINE ('numbers1(' || j || ') = ' || numbers1
(j));
END LOOP;
OPEN c;
FETCH c
BULK COLLECT INTO numbers1;
CLOSE c;
DBMS_OUTPUT.PUT_LINE ('After FETCH
statement');
DBMS_OUTPUT.PUT_LINE ('numbers1.COUNT()
= ' || numbers1.COUNT ());
IF numbers1.COUNT () > 0
THEN
FOR j IN 1 .. numbers1.COUNT ()
LOOP
DBMS_OUTPUT.PUT_LINE ('numbers1(' || j || ') = ' || numbers1
(j));
END LOOP;
END IF;
END p;
/
--Invoke p:
BEGIN
p (2);
END;
/
BEGIN
p (10);
END;
/
--Example
12–20 SECOND Collection Workaround FOR Example 12–18
CREATE OR REPLACE TYPE
numbers_type IS TABLE OF INTEGER
/
CREATE OR REPLACE PROCEDURE p (i IN INTEGER)
AUTHID DEFINER
IS
numbers1
numbers_type
:=
numbers_type (1,
2,
3,
4,
5);
numbers2
numbers_type
:=
numbers_type (0,
0,
0,
0,
0);
BEGIN
DBMS_OUTPUT.PUT_LINE ('Before SELECT
statement');
DBMS_OUTPUT.PUT_LINE ('numbers1.COUNT()
= ' || numbers1.COUNT ());
FOR j IN 1 .. numbers1.COUNT ()
LOOP
DBMS_OUTPUT.PUT_LINE ('numbers1(' || j || ') = ' || numbers1
(j));
END LOOP;
DBMS_OUTPUT.PUT_LINE ('numbers2.COUNT()
= ' || numbers2.COUNT ());
FOR j IN 1 .. numbers2.COUNT ()
LOOP
DBMS_OUTPUT.PUT_LINE ('numbers2(' || j || ') = ' || numbers2
(j));
END LOOP;
SELECT a.COLUMN_VALUE
BULK COLLECT INTO
numbers2 -- numbers2
appears here
FROM TABLE (numbers1) a -- numbers1
appears here
WHERE a.COLUMN_VALUE > p.i
ORDER BY a.COLUMN_VALUE;
DBMS_OUTPUT.PUT_LINE ('After SELECT
statement');
DBMS_OUTPUT.PUT_LINE ('numbers1.COUNT()
= ' || numbers1.COUNT ());
IF numbers1.COUNT () > 0
THEN
FOR j IN 1 .. numbers1.COUNT ()
LOOP
DBMS_OUTPUT.PUT_LINE ('numbers1(' || j || ') = ' || numbers1
(j));
END LOOP;
END IF;
DBMS_OUTPUT.PUT_LINE ('numbers2.COUNT()
= ' || numbers2.COUNT ());
IF numbers2.COUNT () > 0
THEN
FOR j IN 1 .. numbers2.COUNT ()
LOOP
DBMS_OUTPUT.PUT_LINE ('numbers2(' || j || ') = ' || numbers2
(j));
END LOOP;
END IF;
END p;
/
--Invoke p:
BEGIN
p (2);
END;
/
--Invoke P:
BEGIN
p (10);
END;
/
--Example
12–21 Limiting Bulk Selection with ROWNUM, SAMPLE, and FETCH FIRST
DECLARE
TYPE SalList IS TABLE OF employees.salary%TYPE;
sals
SalList;
BEGIN
SELECT salary
BULK COLLECT INTO sals
FROM employees
WHERE ROWNUM <= 50;
SELECT salary
BULK COLLECT INTO sals
FROM employees SAMPLE (10);
SELECT salary
BULK COLLECT INTO sals
FROM employees FETCH FIRST 50 ROWS ONLY;
END;
/
--Example
12–22 BULK-Fetching INTO Two NESTED TABLES
DECLARE
TYPE NameList IS TABLE OF employees.last_name%TYPE;
TYPE SalList IS TABLE OF employees.salary%TYPE;
CURSOR c1
IS
SELECT last_name, salary
FROM employees
WHERE salary > 10000
ORDER BY last_name;
names
NameList;
sals
SalList;
TYPE RecList IS TABLE OF c1%ROWTYPE;
recs
RecList;
v_limit
PLS_INTEGER := 10;
PROCEDURE
print_results
IS
BEGIN
-- Check if collections are
empty:
IF names IS NULL OR names.COUNT = 0
THEN
DBMS_OUTPUT.PUT_LINE ('No results!');
ELSE
DBMS_OUTPUT.PUT_LINE ('Result: ');
FOR i IN names.FIRST .. names.LAST
LOOP
DBMS_OUTPUT.PUT_LINE (
' Employee ' || names (i) || ': $' || sals (i));
END LOOP;
END IF;
END;
BEGIN
DBMS_OUTPUT.PUT_LINE ('---
Processing all results simultaneously ---');
OPEN c1;
FETCH c1
BULK COLLECT INTO names, sals;
CLOSE c1;
print_results ();
DBMS_OUTPUT.PUT_LINE (
'--- Processing ' || v_limit
|| ' rows at a time ---');
OPEN c1;
LOOP
FETCH c1
BULK COLLECT INTO names, sals
LIMIT v_limit;
EXIT WHEN names.COUNT = 0;
print_results ();
END LOOP;
CLOSE c1;
DBMS_OUTPUT.PUT_LINE ('--- Fetching
records rather than columns ---');
OPEN c1;
FETCH c1
BULK COLLECT INTO recs;
FOR i IN recs.FIRST .. recs.LAST
LOOP
-- Now all columns from
result set come from one record
DBMS_OUTPUT.PUT_LINE (
' Employee ' || recs (i).last_name ||
': $' || recs (i).salary);
END LOOP;
END;
/
--Example 12–23
uses a FETCH BULK COLLECT statement to fetch a result set into a collection
(nested table) of records.
--Example
12–23 Bulk-Fetching into Nested Table of Records
DECLARE
CURSOR c1
IS
SELECT first_name, last_name, hire_date FROM employees;
TYPE NameSet IS TABLE OF c1%ROWTYPE;
stock_managers NameSet; -- nested
table of records
TYPE
cursor_var_type IS REF CURSOR;
CV cursor_var_type;
BEGIN
-- Assign values to nested
table of records:
OPEN CV FOR
SELECT first_name, last_name, hire_date
FROM employees
WHERE job_id = 'ST_MAN'
ORDER BY hire_date;
FETCH CV
BULK COLLECT INTO
stock_managers;
CLOSE CV;
-- Print nested table of
records:
FOR i IN
stock_managers.FIRST .. stock_managers.LAST
LOOP
DBMS_OUTPUT.PUT_LINE (
stock_managers (i).hire_date
|| ' '
|| stock_managers (i).last_name
|| ', '
|| stock_managers (i).first_name);
END LOOP;
END;
/
--Example
12–24 Limiting Bulk FETCH with LIMIT
DECLARE
TYPE numtab IS TABLE OF NUMBER
INDEX BY PLS_INTEGER;
CURSOR c1
IS
SELECT employee_id
FROM employees
WHERE
department_id = 80
ORDER BY employee_id;
empids
numtab;
BEGIN
OPEN c1;
LOOP -- Fetch 10
rows or fewer in each iteration
FETCH c1
BULK COLLECT INTO empids
LIMIT 10;
DBMS_OUTPUT.PUT_LINE ('-------
Results from One Bulk Fetch --------');
FOR i IN 1 .. empids.COUNT
LOOP
DBMS_OUTPUT.PUT_LINE ('Employee Id:
' || empids (i));
END LOOP;
EXIT WHEN c1%NOTFOUND;
END LOOP;
CLOSE c1;
END;
/
--Example
12–25 Returning Deleted Rows in Two Nested Tables
DROP TABLE emp_temp;
CREATE TABLE emp_temp
AS
SELECT *
FROM employees
ORDER BY employee_id;
DECLARE
TYPE NumList IS TABLE OF employees.employee_id%TYPE;
enums
NumList;
TYPE NameList IS TABLE OF employees.last_name%TYPE;
names
NameList;
BEGIN
DELETE FROM emp_temp
WHERE
department_id = 30
RETURNING employee_id, last_name
BULK COLLECT INTO enums, names;
DBMS_OUTPUT.PUT_LINE ('Deleted ' || SQL%ROWCOUNT || ' rows:');
FOR i IN enums.FIRST .. enums.LAST
LOOP
DBMS_OUTPUT.PUT_LINE ('Employee #' || enums (i) || ': ' || names (i));
END LOOP;
END;
/