文章目錄
1 概述
bulk collect
子句會批量檢索結果,即一次性將結果集綁定到一個集合變量中,並從 SQL引擎發送到 PL/SQL引擎。bulk collect into
的目標對象必須是 集合類型
關鍵字 | 解釋 | 鏈接 |
---|---|---|
forall | 用於增強 PL/SQL 引擎到 SQL 引擎的交換 | PL/SQL foall 詳解 |
bulk collect | 用於增強 SQL 引擎到 PL/SQL 引擎的交換 |
2 使用
基礎數據準備:
CREATE TABLE stu_info AS (
id number(3),
name varchar2(30),
sex varchar(2)
);
INSERT INTO stu_info(ID, NAME, sex) VALUES(1, '瑤瑤', '女');
INSERT INTO stu_info(ID, NAME, sex) VALUES(2, '優優', '男');
INSERT INTO stu_info(ID, NAME, sex) VALUES(3, '倩倩', '女');
COMMIT;
2.1 在 select into 中
DECLARE
-- 定義記錄類型
TYPE stu_info_record IS RECORD(
id stu_info.id%TYPE,
NAME stu_info.name%TYPE,
sex stu_info.sex%TYPE);
-- 定義基於記錄類型的嵌套表
TYPE stu_info_table IS TABLE OF stu_info_record;
-- 聲明變量
v_stu_info_table stu_info_table;
BEGIN
-- 使用 bulk collect 將所得的結果集一次綁定到記錄記錄變量中
SELECT si.id, si.name, si.sex
BULK COLLECT
INTO v_stu_info_table
FROM stu_info si;
-- 輸出驗證
FOR i IN v_stu_info_table.first .. v_stu_info_table.last LOOP
dbms_output.put_line('id: ' || v_stu_info_table(i).id || ', name: ' || v_stu_info_table(i).name ||
', sex: ' || v_stu_info_table(i).sex);
END LOOP;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line(dbms_utility.format_error_backtrace);
dbms_output.put_line(SQLCODE || ', ' || SQLERRM);
END;
輸出結果:
id: 1, name: 瑤瑤, sex: 女
id: 2, name: 優優, sex: 男
id: 3, name: 倩倩, sex: 女
2.2 在 fetch into 中
- 在遊標中可以使用 bulk collect 一次取出一個數據集合,比單條取數據效率高。
語法:
fetch ... bulk collect into ... [limit row_number]
- 在使用 bulk collect 子句時,對於集合類型會 自動 對其進行初始化以及擴展。
- 爲避免過大的數據集造成性能下降,使用
limit
子句來限制一次提取的數據量。
DECLARE
-- 定義記錄類型
TYPE stu_info_record IS RECORD(
id stu_info.id%TYPE,
NAME stu_info.name%TYPE,
sex stu_info.sex%TYPE);
-- 定義基於記錄類型的嵌套表
TYPE stu_info_table IS TABLE OF stu_info_record;
-- 聲明變量
v_stu_info_table stu_info_table;
-- 聲明遊標
CURSOR cur_stu_info IS
SELECT si.id, si.name, si.sex FROM stu_info si;
-- 定義變量來記錄 fetch 的次數
v_fetch_count PLS_INTEGER := 0;
BEGIN
-- 開啓遊標
OPEN cur_stu_info;
LOOP
-- fetch 時使用 bulk collect 子句
FETCH cur_stu_info BULK COLLECT
INTO v_stu_info_table LIMIT 2; -- 數據有限,僅做測試,一般限制 500 左右
EXIT WHEN v_stu_info_table.count = 0; -- 注意此時遊標退出使用了 xx.count,而不是 xx%notfound
v_fetch_count := v_fetch_count + 1;
-- 輸出驗證
FOR i IN v_stu_info_table.first .. v_stu_info_table.last LOOP
dbms_output.put_line('id: ' || v_stu_info_table(i).id ||
', name: ' || v_stu_info_table(i).name ||
', sex: ' || v_stu_info_table(i).sex);
END LOOP;
END LOOP;
-- 關閉遊標
CLOSE cur_stu_info;
dbms_output.put_line('總共獲取的次數:' || v_fetch_count);
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line(dbms_utility.format_error_backtrace);
dbms_output.put_line(SQLCODE || ', ' || SQLERRM);
-- 異常時,也要關閉遊標
IF cur_stu_info%ISOPEN THEN
CLOSE cur_stu_info;
END IF;
END;
輸出結果:
id: 1, name: 瑤瑤, sex: 女
id: 2, name: 優優, sex: 男
id: 3, name: 倩倩, sex: 女
總共獲取的次數:2 -- 總共 3 條記錄,第一次獲取 2 條(limit 2), 第二次獲取 1 條
2.3 在 returning into 中
- 當執行
insert、update、delete
時,可使用returning
子句來批量綁定。
DECLARE
-- 定義記錄類型
TYPE stu_info_record IS RECORD(
id stu_info.id%TYPE,
NAME stu_info.name%TYPE,
sex stu_info.sex%TYPE);
-- 定義基於記錄類型的嵌套表
TYPE stu_info_table IS TABLE OF stu_info_record;
-- 聲明變量
v_stu_info_table stu_info_table;
BEGIN
-- delete 語句,insert,update 同理
DELETE FROM stu_info si
WHERE si.sex = '女'
RETURNING si.id, si.name, si.sex BULK COLLECT INTO v_stu_info_table;
dbms_output.put_line('刪除了 ' || SQL%ROWCOUNT || ' 行記錄');
COMMIT;
-- 輸出驗證
IF v_stu_info_table.count > 0 THEN
FOR i IN v_stu_info_table.first .. v_stu_info_table.last LOOP
dbms_output.put_line('id: ' || v_stu_info_table(i).id ||
', name: ' || v_stu_info_table(i).name ||
', sex: ' || v_stu_info_table(i).sex);
END LOOP;
END IF;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line(dbms_utility.format_error_backtrace);
dbms_output.put_line(SQLCODE || ', ' || SQLERRM);
END;
輸出結果:
刪除了 2 行記錄
id: 1, name: 瑤瑤, sex: 女
id: 3, name: 倩倩, sex: 女
3 擴展:forall + bulk collect 綜合運用
需求:將 stu_info 中的數據同步至 stu_info_temp
基礎數據準備:
TRUNCATE TABLE stu_info ;
INSERT INTO stu_info(ID, NAME, sex) VALUES(1, '瑤瑤', '女');
INSERT INTO stu_info(ID, NAME, sex) VALUES(2, '優優', '男');
INSERT INTO stu_info(ID, NAME, sex) VALUES(3, '倩倩', '女');
COMMIT;
-- 測試表
CREATE TABLE stu_info_temp AS SELECT * from stu_info WHERE 1 = 2;
實戰:
DECLARE
-- 聲明遊標
CURSOR cur_stu_info IS
SELECT si.id, si.name, si.sex FROM stu_info si;
-- 定義基於遊標類型的嵌套表 (也可用 RECORD 替換,只是寫法會稍微麻煩點)
TYPE stu_info_table IS TABLE OF cur_stu_info%ROWTYPE;
-- 聲明變量
v_stu_info_table stu_info_table;
BEGIN
-- 開啓遊標
OPEN cur_stu_info;
LOOP
-- fetch 時使用 bulk collect 子句
FETCH cur_stu_info BULK COLLECT
INTO v_stu_info_table LIMIT 2; -- 數據有限,僅做測試,一般限制 500 左右
EXIT WHEN v_stu_info_table.count = 0; -- 注意此時遊標退出使用了 xx.count,而不是 xx%notfound
-- 批量插入
FORALL i IN 1 .. v_stu_info_table.count
-- INSERT INTO stu_info_temp (id, NAME, sex) VALUES v_stu_info (i); -- ORA-00947: 沒有足夠的值
INSERT INTO
(SELECT sit.id, sit.name, sit.sex FROM stu_info_temp sit)
VALUES v_stu_info_table
(i);
END LOOP;
-- 關閉遊標
CLOSE cur_stu_info;
-- 提交數據
COMMIT;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line(dbms_utility.format_error_backtrace);
dbms_output.put_line(SQLCODE || ', ' || SQLERRM);
-- 異常時,也要關閉遊標
IF cur_stu_info%ISOPEN THEN
CLOSE cur_stu_info;
END IF;
END;