返回由大量行組成的結果集的 SQL 命令可能無法儘可能高效地運行,因為必須在資料庫伺服器和用戶端之間進行常量環境切換才能傳輸整個結果集。通過使用集合將整個結果集收集到用戶端隨後可訪問的記憶體中,可減輕這一低效率問題。BULK COLLECT 子句用於指定結果集到集合的這種彙總。
BULK COLLECT 子句可與 SELECT INTO、FETCH INTO 和 EXECUTEIMMEDIATE 命令一起使用,也可與 DELETE、INSERT 和 UPDATE 命令的 RETURNING INTO 子句一起使用。其中每個語句和子句將在以下各節中予以說明。
SELECT BULK COLLECT
BULK COLLECT 子句可與 SELECT INTO 語句一起使用,如下所示。
SELECT select_expressions BULK COLLECT INTO collection
[, ...] FROM ...;如果指定了單個集合,則 collection 可以是單個欄位的集合,也可以是某一記錄類型的集合。如果指定了多個集合,則每個 collection 必須由單個欄位組成。select_expressions 必須在數字、順序和類型相容性方面與目的地組合中的所有欄位相匹配。
以下樣本顯示如何使用 BULKCOLLECT 子句,其中目的地組合是由單個欄位組成的關聯陣列。
DECLARE
TYPE empno_tbl IS TABLE OF emp.empno%TYPE INDEX BY BINARY_INTEGER;
TYPE ename_tbl IS TABLE OF emp.ename%TYPE INDEX BY BINARY_INTEGER;
TYPE job_tbl IS TABLE OF emp.job%TYPE INDEX BY BINARY_INTEGER;
TYPE hiredate_tbl IS TABLE OF emp.hiredate%TYPE INDEX BY BINARY_INTEGER;
TYPE sal_tbl IS TABLE OF emp.sal%TYPE INDEX BY BINARY_INTEGER;
TYPE comm_tbl IS TABLE OF emp.comm%TYPE INDEX BY BINARY_INTEGER;
TYPE deptno_tbl IS TABLE OF emp.deptno%TYPE INDEX BY BINARY_INTEGER;
t_empno EMPNO_TBL;
t_ename ENAME_TBL;
t_job JOB_TBL;
t_hiredate HIREDATE_TBL;
t_sal SAL_TBL;
t_comm COMM_TBL;
t_deptno DEPTNO_TBL;
BEGIN
SELECT empno, ename, job, hiredate, sal, comm, deptno BULK COLLECT
INTO t_empno, t_ename, t_job, t_hiredate, t_sal, t_comm, t_deptno
FROM emp;
DBMS_OUTPUT.PUT_LINE('EMPNO ENAME JOB HIREDATE ' ||
'SAL ' || 'COMM DEPTNO');
DBMS_OUTPUT.PUT_LINE('----- ------- --------- --------- ' ||
'-------- ' || '-------- ------');
FOR i IN 1..t_empno.COUNT LOOP
DBMS_OUTPUT.PUT_LINE(t_empno(i) || ' ' ||
RPAD(t_ename(i),8) || ' ' ||
RPAD(t_job(i),10) || ' ' ||
TO_CHAR(t_hiredate(i),'DD-MON-YY') || ' ' ||
TO_CHAR(t_sal(i),'99,999.99') || ' ' ||
TO_CHAR(NVL(t_comm(i),0),'99,999.99') || ' ' ||
t_deptno(i));
END LOOP;
END;
EMPNO ENAME JOB HIREDATE SAL COMM DEPTNO
----- ------- --------- --------- -------- -------- ------
7369 SMITH CLERK 17-DEC-80 800.00 .00 20
7499 ALLEN SALESMAN 20-FEB-81 1,600.00 300.00 30
7521 WARD SALESMAN 22-FEB-81 1,250.00 500.00 30
7566 JONES MANAGER 02-APR-81 2,975.00 .00 20
7654 MARTIN SALESMAN 28-SEP-81 1,250.00 1,400.00 30
7698 BLAKE MANAGER 01-MAY-81 2,850.00 .00 30
7782 CLARK MANAGER 09-JUN-81 2,450.00 .00 10
7788 SCOTT ANALYST 19-APR-87 3,000.00 .00 20
7839 KING PRESIDENT 17-NOV-81 5,000.00 .00 10
7844 TURNER SALESMAN 08-SEP-81 1,500.00 .00 30
7876 ADAMS CLERK 23-MAY-87 1,100.00 .00 20
7900 JAMES CLERK 03-DEC-81 950.00 .00 30
7902 FORD ANALYST 03-DEC-81 3,000.00 .00 20
7934 MILLER CLERK 23-JAN-82 1,300.00 .00 10以下樣本產生相同的結果,但對使用 %ROWTYPE 屬性定義的記錄類型使用了關聯陣列。
DECLARE
TYPE emp_tbl IS TABLE OF emp%ROWTYPE INDEX BY BINARY_INTEGER;
t_emp EMP_TBL;
BEGIN
SELECT * BULK COLLECT INTO t_emp FROM emp;
DBMS_OUTPUT.PUT_LINE('EMPNO ENAME JOB HIREDATE ' ||
'SAL ' || 'COMM DEPTNO');
DBMS_OUTPUT.PUT_LINE('----- ------- --------- --------- ' ||
'-------- ' || '-------- ------');
FOR i IN 1..t_emp.COUNT LOOP
DBMS_OUTPUT.PUT_LINE(t_emp(i).empno || ' ' ||
RPAD(t_emp(i).ename,8) || ' ' ||
RPAD(t_emp(i).job,10) || ' ' ||
TO_CHAR(t_emp(i).hiredate,'DD-MON-YY') || ' ' ||
TO_CHAR(t_emp(i).sal,'99,999.99') || ' ' ||
TO_CHAR(NVL(t_emp(i).comm,0),'99,999.99') || ' ' ||
t_emp(i).deptno);
END LOOP;
END;
EMPNO ENAME JOB HIREDATE SAL COMM DEPTNO
----- ------- --------- --------- -------- -------- ------
7369 SMITH CLERK 17-DEC-80 800.00 .00 20
7499 ALLEN SALESMAN 20-FEB-81 1,600.00 300.00 30
7521 WARD SALESMAN 22-FEB-81 1,250.00 500.00 30
7566 JONES MANAGER 02-APR-81 2,975.00 .00 20
7654 MARTIN SALESMAN 28-SEP-81 1,250.00 1,400.00 30
7698 BLAKE MANAGER 01-MAY-81 2,850.00 .00 30
7782 CLARK MANAGER 09-JUN-81 2,450.00 .00 10
7788 SCOTT ANALYST 19-APR-87 3,000.00 .00 20
7839 KING PRESIDENT 17-NOV-81 5,000.00 .00 10
7844 TURNER SALESMAN 08-SEP-81 1,500.00 .00 30
7876 ADAMS CLERK 23-MAY-87 1,100.00 .00 20
7900 JAMES CLERK 03-DEC-81 950.00 .00 30
7902 FORD ANALYST 03-DEC-81 3,000.00 .00 20
7934 MILLER CLERK 23-JAN-82 1,300.00 .00 10FETCH BULK COLLECT
BULK COLLECT 子句可與 FETCH 語句一起使用。FETCH BULK COLLECT 不是一次從結果集返回一行,而是將結果集中的所有行一次性返回到指定的集合中,除非由 LIMIT 子句進行了限制。
FETCH name BULK COLLECT INTO collection [, ...] [ LIMIT n ];如果指定了單個集合,則 collection 可以是單個欄位的集合,也可以是某一記錄類型的集合。如果指定了多個集合,則每個 collection 必須由單個欄位組成。遊標(由 name 標識)的 SELECT 列表中的運算式必須在數字、順序和類型相容性方面與目的地組合中的所有欄位相匹配。如果指定了 LIMIT n,則每個 FETCH 返回到集合中的行數將不會超過 n。
以下樣本使用 FETCHBULK COLLECT 語句將行檢索到關聯陣列中。
DECLARE
TYPE emp_tbl IS TABLE OF emp%ROWTYPE INDEX BY BINARY_INTEGER;
t_emp EMP_TBL;
CURSOR emp_cur IS SELECT * FROM emp;
BEGIN
OPEN emp_cur;
FETCH emp_cur BULK COLLECT INTO t_emp;
CLOSE emp_cur;
DBMS_OUTPUT.PUT_LINE('EMPNO ENAME JOB HIREDATE ' ||
'SAL ' || 'COMM DEPTNO');
DBMS_OUTPUT.PUT_LINE('----- ------- --------- --------- ' ||
'-------- ' || '-------- ------');
FOR i IN 1..t_emp.COUNT LOOP
DBMS_OUTPUT.PUT_LINE(t_emp(i).empno || ' ' ||
RPAD(t_emp(i).ename,8) || ' ' ||
RPAD(t_emp(i).job,10) || ' ' ||
TO_CHAR(t_emp(i).hiredate,'DD-MON-YY') || ' ' ||
TO_CHAR(t_emp(i).sal,'99,999.99') || ' ' ||
TO_CHAR(NVL(t_emp(i).comm,0),'99,999.99') || ' ' ||
t_emp(i).deptno);
END LOOP;
END;
EMPNO ENAME JOB HIREDATE SAL COMM DEPTNO
----- ------- --------- --------- -------- -------- ------
7369 SMITH CLERK 17-DEC-80 800.00 .00 20
7499 ALLEN SALESMAN 20-FEB-81 1,600.00 300.00 30
7521 WARD SALESMAN 22-FEB-81 1,250.00 500.00 30
7566 JONES MANAGER 02-APR-81 2,975.00 .00 20
7654 MARTIN SALESMAN 28-SEP-81 1,250.00 1,400.00 30
7698 BLAKE MANAGER 01-MAY-81 2,850.00 .00 30
7782 CLARK MANAGER 09-JUN-81 2,450.00 .00 10
7788 SCOTT ANALYST 19-APR-87 3,000.00 .00 20
7839 KING PRESIDENT 17-NOV-81 5,000.00 .00 10
7844 TURNER SALESMAN 08-SEP-81 1,500.00 .00 30
7876 ADAMS CLERK 23-MAY-87 1,100.00 .00 20
7900 JAMES CLERK 03-DEC-81 950.00 .00 30
7902 FORD ANALYST 03-DEC-81 3,000.00 .00 20
7934 MILLER CLERK 23-JAN-82 1,300.00 .00 10EXECUTE IMMEDIATE BULK COLLECT
BULK COLLECT 子句可與 EXECUTE IMMEDIATE 語句一起使用以指定要接收返回行的集合。
EXECUTE IMMEDIATE 'sql_expression;'
BULK COLLECT INTO collection [,...]
[USING {[bind_type] bind_argument} [, ...]}];collection 指定集合的名稱。
bind_type 指定 bind_argument 的參數模式。
- bind_type 為 IN 時指定 bind_argument 包含傳遞給 sql_expression 的值。
- bind_type 為 OUT 時指定 bind_argument 從 sql_expression 接收值。
- bind_type 為 IN OUT 時指定將 bind_argument 傳遞給 sql_expression,然後儲存 sql_expression 返回的值。
bind_argument 指定一個參數,該參數包含一個值,該值或傳遞給 sql_expression(bind_type 為 IN 時指定),或從 sql_expression 接收值(bind_type 為 OUT 時指定),或者執行這兩項操作(bind_type 為 INOUT 時指定)。
如果指定了單個集合,則 collection 可以是單個欄位的集合,也可以是某一記錄類型的集合;如果指定了多個集合,則每個 collection 必須由單個欄位組成。
RETURNING BULK COLLECT
BULK COLLECT 子句可添加到 DELETE、INSERT 或 UPDATE 命令的 RETURNING INTO 子句。
{ insert | update | delete }
RETURNING { * | expr_1 [, expr_2 ] ...}
BULK COLLECT INTO collection [, ...];insert、update 和 delete 分別是 INSERT、UPDATE 和 DELETE 命令,如相應的INSERT、UPDATE和DELETE中所述。如果指定了單個集合,則 collection 可以是單個欄位的集合,也可以是某一記錄類型的集合。如果指定了多個集合,則每個 collection 必須由單個欄位組成。RETURNING 關鍵字後面的運算式必須在數字、順序和類型相容性方面與目的地組合中的所有欄位相匹配。如果指定了 *,則將返回受影響表中的所有列。(請注意,使用 * 是PolarDB PostgreSQL版(相容Oracle)擴充,與 Oracle 資料庫不相容。)
通過複製 emp 表建立的 clerkemp 表將用在本節的其餘樣本中,如下所示。
CREATE TABLE clerkemp AS SELECT * FROM emp WHERE job = 'CLERK';
SELECT * FROM clerkemp;
empno | ename | job | mgr | hiredate | sal | comm | deptno
-------+--------+-------+------+--------------------+---------+------+--------
7369 | SMITH | CLERK | 7902 | 17-DEC-80 00:00:00 | 800.00 | | 20
7876 | ADAMS | CLERK | 7788 | 23-MAY-87 00:00:00 | 1100.00 | | 20
7900 | JAMES | CLERK | 7698 | 03-DEC-81 00:00:00 | 950.00 | | 30
7934 | MILLER | CLERK | 7782 | 23-JAN-82 00:00:00 | 1300.00 | | 10
(4 rows)以下樣本將每名員工的工資增加 1.5 倍,將員工的編號、姓名和新工資儲存在三個關聯陣列中,並在最後顯示這些數組的內容。
DECLARE
TYPE empno_tbl IS TABLE OF emp.empno%TYPE INDEX BY BINARY_INTEGER;
TYPE ename_tbl IS TABLE OF emp.ename%TYPE INDEX BY BINARY_INTEGER;
TYPE sal_tbl IS TABLE OF emp.sal%TYPE INDEX BY BINARY_INTEGER;
t_empno EMPNO_TBL;
t_ename ENAME_TBL;
t_sal SAL_TBL;
BEGIN
UPDATE clerkemp SET sal = sal * 1.5 RETURNING empno, ename, sal
BULK COLLECT INTO t_empno, t_ename, t_sal;
DBMS_OUTPUT.PUT_LINE('EMPNO ENAME SAL ');
DBMS_OUTPUT.PUT_LINE('----- ------- -------- ');
FOR i IN 1..t_empno.COUNT LOOP
DBMS_OUTPUT.PUT_LINE(t_empno(i) || ' ' || RPAD(t_ename(i),8) ||
' ' || TO_CHAR(t_sal(i),'99,999.99'));
END LOOP;
END;
EMPNO ENAME SAL
----- ------- --------
7369 SMITH 1,200.00
7876 ADAMS 1,650.00
7900 JAMES 1,425.00
7934 MILLER 1,950.00以下樣本執行與上一樣本相同的功能,但使用單個集合(使用某一記錄類型定義)來儲存員工的編號、姓名和新工資。
DECLARE
TYPE emp_rec IS RECORD (
empno emp.empno%TYPE,
ename emp.ename%TYPE,
sal emp.sal%TYPE
);
TYPE emp_tbl IS TABLE OF emp_rec INDEX BY BINARY_INTEGER;
t_emp EMP_TBL;
BEGIN
UPDATE clerkemp SET sal = sal * 1.5 RETURNING empno, ename, sal
BULK COLLECT INTO t_emp;
DBMS_OUTPUT.PUT_LINE('EMPNO ENAME SAL ');
DBMS_OUTPUT.PUT_LINE('----- ------- -------- ');
FOR i IN 1..t_emp.COUNT LOOP
DBMS_OUTPUT.PUT_LINE(t_emp(i).empno || ' ' ||
RPAD(t_emp(i).ename,8) || ' ' ||
TO_CHAR(t_emp(i).sal,'99,999.99'));
END LOOP;
END;
EMPNO ENAME SAL
----- ------- --------
7369 SMITH 1,200.00
7876 ADAMS 1,650.00
7900 JAMES 1,425.00
7934 MILLER 1,950.00以下樣本從 clerkemp 表中刪除所有行,然後將有關已刪除行的資訊返回到隨後顯示的關聯陣列中。
DECLARE
TYPE emp_rec IS RECORD (
empno emp.empno%TYPE,
ename emp.ename%TYPE,
job emp.job%TYPE,
hiredate emp.hiredate%TYPE,
sal emp.sal%TYPE,
comm emp.comm%TYPE,
deptno emp.deptno%TYPE
);
TYPE emp_tbl IS TABLE OF emp_rec INDEX BY BINARY_INTEGER;
r_emp EMP_TBL;
BEGIN
DELETE FROM clerkemp RETURNING empno, ename, job, hiredate, sal,
comm, deptno BULK COLLECT INTO r_emp;
DBMS_OUTPUT.PUT_LINE('EMPNO ENAME JOB HIREDATE ' ||
'SAL ' || 'COMM DEPTNO');
DBMS_OUTPUT.PUT_LINE('----- ------- --------- --------- ' ||
'-------- ' || '-------- ------');
FOR i IN 1..r_emp.COUNT LOOP
DBMS_OUTPUT.PUT_LINE(r_emp(i).empno || ' ' ||
RPAD(r_emp(i).ename,8) || ' ' ||
RPAD(r_emp(i).job,10) || ' ' ||
TO_CHAR(r_emp(i).hiredate,'DD-MON-YY') || ' ' ||
TO_CHAR(r_emp(i).sal,'99,999.99') || ' ' ||
TO_CHAR(NVL(r_emp(i).comm,0),'99,999.99') || ' ' ||
r_emp(i).deptno);
END LOOP;
END;
EMPNO ENAME JOB HIREDATE SAL COMM DEPTNO
----- ------- --------- --------- -------- -------- ------
7369 SMITH CLERK 17-DEC-80 1,200.00 .00 20
7876 ADAMS CLERK 23-MAY-87 1,650.00 .00 20
7900 JAMES CLERK 03-DEC-81 1,425.00 .00 30
7934 MILLER CLERK 23-JAN-82 1,950.00 .00 10