提高性能的BULK COLLECT语句和FORALL语句 IN PLSQL

提高性能的BULK COLLECT语句和FORALL语句

pl/sql向sql引擎发送sql语句执行,然后SQL向pl/sql 返回结果数据。可以使用pl/sql的大批量sql特性来降低sql和pl /sql之间的通信开销。FORALL语句将INSERT或UPDATE或DELETE批次处理,BULK COLLECT子句带回批结果。如果,DML语句影响四个或更多个数据库行,使用bulk sql(批量SQL)可以显著提高性能。

1.FORALL语句的使用

但是,FORALL语句只能重复单独一条 DML语句。 例子:FORALL语句一次性把三条DELETE 语句发给SQL引擎:

复制代码
 1  CREATE TABLE employees_temp AS SELECT * FROM employees;  2 DECLARE  3 TYPE NumList IS VARRAY(20) OF NUMBER;  4 depts NumList := NumList(10, 30, 70); -- department numbers  5 BEGIN  6 FORALL i IN depts.FIRST..depts.LAST  7 DELETE FROM employees_temp WHERE department_id = depts(i);  8 COMMIT;  9 END; 10 /
复制代码

 我们可以通过下面的例子来比较FOR语句和FORALL语句的执行时间:

复制代码
 1  CREATE TABLE parts1 (pnum INTEGER, pname VARCHAR2(15));  2 CREATE TABLE parts2 (pnum INTEGER, pname VARCHAR2(15));  3 DECLARE  4 TYPE NumTab IS TABLE OF parts1.pnum%TYPE INDEX BY PLS_INTEGER;  5 TYPE NameTab IS TABLE OF parts1.pname%TYPE INDEX BY PLS_INTEGER;  6  pnums NumTab;  7  pnames NameTab;  8 iterations CONSTANT PLS_INTEGER := 500;  9 t1 INTEGER; 10 t2 INTEGER; 11 t3 INTEGER; 12 BEGIN 13 FOR j IN 1..iterations LOOP -- load index-by tables 14 pnums(j) := j; 15 pnames(j) := 'Part No. ' || TO_CHAR(j); 16 END LOOP; 17 t1 := DBMS_UTILITY.get_time; 18 FOR i IN 1..iterations LOOP -- use FOR loop 19 INSERT INTO parts1 VALUES (pnums(i), pnames(i)); 20 END LOOP; 21 t2 := DBMS_UTILITY.get_time; 22 FORALL i IN 1..iterations -- use FORALL statement 23 INSERT INTO parts2 VALUES (pnums(i), pnames(i)); 24 t3 := DBMS_UTILITY.get_time; 25 DBMS_OUTPUT.PUT_LINE('Execution Time (secs)'); 26 DBMS_OUTPUT.PUT_LINE('---------------------'); 27 DBMS_OUTPUT.PUT_LINE('FOR loop: ' || TO_CHAR((t2 - t1)/100)); 28 DBMS_OUTPUT.PUT_LINE('FORALL: ' || TO_CHAR((t3 - t2)/100)); 29 COMMIT; 30 END; 31 / 32  Execution Time (secs) 33 --------------------- 34 FOR loop: .02 35 FORALL: 0
复制代码

由此,我们可以看出使用FORALL语句更快。

2.使用部分索引的FORALL语句(Part of a Collection)

 此外,我们可以只使用条件集合的部分用于FORALL条件。如:

复制代码
 1  CREATE TABLE employees_temp AS SELECT * FROM employees;  2 DECLARE  3 TYPE NumList IS VARRAY(10) OF NUMBER;  4 depts NumList := NumList(5,10,20,30,50,55,57,60,70,75);  5 BEGIN  6 FORALL j IN 4..7 -- use only part of varray  7 DELETE FROM employees_temp WHERE department_id = depts(j);  8 COMMIT;  9 END; 10 /
复制代码

3.使用非连续的稀疏索引的FORALL语句(Non-Consecutive Index Values)
  同样,我们也可以使用非连续的index value(索引值)作为条件,这需要使用INDICES OF子句来处理。

复制代码
 1  -- Create empty tables to hold order details
 2  CREATE TABLE valid_orders (cust_name VARCHAR2(32), amount NUMBER(10,2));  3 CREATE TABLE big_orders AS SELECT * FROM valid_orders WHERE 1 = 0;  4 CREATE TABLE rejected_orders AS SELECT * FROM valid_orders WHERE 1 = 0;  5 DECLARE  6 -- Make collections to hold a set of customer names and order amounts.  7 SUBTYPE cust_name IS valid_orders.cust_name%TYPE;  8 TYPE cust_typ IS TABLE OF cust_name;  9  cust_tab cust_typ; 10 SUBTYPE order_amount IS valid_orders.amount%TYPE; 11 TYPE amount_typ IS TABLE OF NUMBER; 12  amount_tab amount_typ; 13 -- Make other collections to point into the CUST_TAB collection. 14 TYPE index_pointer_t IS TABLE OF PLS_INTEGER; 15 big_order_tab index_pointer_t := index_pointer_t(); 16 rejected_order_tab index_pointer_t := index_pointer_t(); 17 PROCEDURE setup_data IS BEGIN 18 -- Set up sample order data, including some invalid orders and some 'big' orders. 19 cust_tab := cust_typ('Company1','Company2','Company3','Company4','Company5'); 20 amount_tab := amount_typ(5000.01, 0, 150.25, 4000.00, NULL); 21 END; 22 BEGIN 23  setup_data(); 24 DBMS_OUTPUT.PUT_LINE('--- Original order data ---'); 25 FOR i IN 1..cust_tab.LAST LOOP 26 DBMS_OUTPUT.PUT_LINE('Customer #' || i || ', ' || cust_tab(i) || ': $' || 27  amount_tab(i)); 28 END LOOP; 29 -- Delete invalid orders (where amount is null or 0). 30 FOR i IN 1..cust_tab.LAST LOOP 31 IF amount_tab(i) is null or amount_tab(i) = 0 THEN 32 cust_tab.delete(i); 33 amount_tab.delete(i); 34 END IF; 35 END LOOP; 36 DBMS_OUTPUT.PUT_LINE('--- Data with invalid orders deleted ---'); 37 FOR i IN 1..cust_tab.LAST LOOP 38 IF cust_tab.EXISTS(i) THEN 39 DBMS_OUTPUT.PUT_LINE('Customer #' || i || ', ' || cust_tab(i) || ': $' || 40 amount_tab(i)); 41 END IF; 42 END LOOP; 43 -- Because the subscripts of the collections are not consecutive, use 44 -- FORALL...INDICES OF to iterate through the actual subscripts, 45 -- rather than 1..COUNT 46 FORALL i IN INDICES OF cust_tab 47 INSERT INTO valid_orders(cust_name, amount) 48 VALUES(cust_tab(i), amount_tab(i)); 49 -- Now process the order data differently 50 -- Extract 2 subsets and store each subset in a different table 51 setup_data(); -- Initialize the CUST_TAB and AMOUNT_TAB collections again. 52 FOR i IN cust_tab.FIRST .. cust_tab.LAST LOOP 53 IF amount_tab(i) IS NULL OR amount_tab(i) = 0 THEN 54 rejected_order_tab.EXTEND; -- Add a new element to this collection 55 -- Record the subscript from the original collection 56 rejected_order_tab(rejected_order_tab.LAST) := i; 57 END IF; 58 IF amount_tab(i) > 2000 THEN 59 big_order_tab.EXTEND; -- Add a new element to this collection 60 -- Record the subscript from the original collection 61 big_order_tab(big_order_tab.LAST) := i; 62 END IF; 63 END LOOP; 64 -- Now it's easy to run one DML statement on one subset of elements, 65 -- and another DML statement on a different subset. 66 FORALL i IN VALUES OF rejected_order_tab 67 INSERT INTO rejected_orders VALUES (cust_tab(i), amount_tab(i)); 68 FORALL i IN VALUES OF big_order_tab 69 INSERT INTO big_orders VALUES (cust_tab(i), amount_tab(i)); 70 COMMIT; 71 END; 72 / 73 -- Verify that the correct order details were stored 74 SELECT cust_name "Customer", amount "Valid order amount" FROM valid_orders; 75 SELECT cust_name "Customer", amount "Big order amount" FROM big_orders; 76 SELECT cust_name "Customer", amount "Rejected order amount" FROM rejected_orders;
复制代码

转载于:https://www.cnblogs.com/Jeffrey-xu/p/5057715.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值