Bulk SQL Operations

       PL/SQL中的批量操作,允许一次操作和处理多行数据,而不是一次处理一行数据。批量操作包括批量的读取数据,批量的绑定处理处理数据:( bulk fetching and bulk binding)

      Bulk fetching refers to the ability to retrieve a set of rows from the database into PL/SQL structures with a single call, rather than making a call to the database for each row to be retrieved.

      Bulk binding lets you perform the converse: to take those sets of rows stored in
PL/SQL structures and save them to the database in an efficient manner.
   

-------------------------------------------------------------------------------------------------

     Getting Started with BULK Fetch

在PL/SQL中,有三种方法获取行记录:

1、Implicit Cursor

    SELECT INTO

    (当没有查询到记录,或者查询到的记录多于一行会抛出异常--NO_DATA_FOUND和TO_MANY_ROWS)

1 DECLARE
2   l_Descr Hardware.Descr%TYPE;
3 BEGIN
4   SELECT Descr
5     INTO l_Descr
6     FROM Hardware
7    WHERE Aisle = 1
8      AND Item = 1;
9 END;

2. Explicit Fetch Calls(手动i打开游标,进行游标的关闭和读取数据)

 1 DECLARE
 2   CURSOR c_Tool_List IS
 3     SELECT Descr
 4       FROM Hardware
 5      WHERE Aisle = 1
 6        AND Item BETWEEN 1 AND 500;
 7   l_Descr Hardware.Descr%TYPE;
 8 BEGIN
 9   OPEN c_Tool_List;
10   LOOP
11     FETCH c_Tool_List
12       INTO l_Descr;
13     EXIT WHEN c_Tool_List%NOTFOUND;
14   END LOOP;
15   CLOSE c_Tool_List;
16 END;

3、Implicit Fetch Calls(FOR LOOP循环,游标FOR循环--推荐使用,因为不要进行游标的关闭等)

1 BEGIN
2   FOR i IN (SELECT Descr
3               FROM Hardware
4              WHERE Aisle = 1
5                AND Item BETWEEN 1 AND 500) LOOP
6   <processing code for each row>
7   END LOOP;
8 END;

 上述三种情况对应的批量读取数据:

1. Implicit Cursor BULK Mode:SELECT  BULK COLLECT INTO

 1 DECLARE
 2   TYPE t_Descr_List IS TABLE OF Hardware.Descr%TYPE;
 3   l_Descr_List t_Descr_List;
 4 BEGIN
 5   SELECT Descr BULK COLLECT
 6     INTO l_Descr_List
 7     FROM Hardware
 8    WHERE Aisle = 1
 9      AND Item BETWEEN 1 AND 100;
10 END;

2. Explicit Fetch Calls BULK Mode:FETCH BULK COLLECT INTO

 1 DECLARE
 2   CURSOR c_Tool_List IS
 3     SELECT Descr
 4       FROM Hardware
 5      WHERE Aisle = 1
 6        AND Item BETWEEN 1 AND 500;
 7   TYPE t_Descr_List IS TABLE OF c_Tool_List%ROWTYPE;
 8   l_Descr_List t_Descr_List;
 9 BEGIN
10   OPEN c_Tool_List;
11   FETCH c_Tool_List BULK COLLECT
12     INTO l_Descr_List;
13   CLOSE c_Tool_List;
14 END;

在这种情况下,假如使用LIMIT参数,限制每次处理的记录条数:

 1 DECLARE
 2   TYPE Numtab IS TABLE OF NUMBER INDEX BY PLS_INTEGER;
 3   CURSOR C1 IS
 4     SELECT Employee_Id FROM Employees WHERE Department_Id = 80;
 5   Empids Numtab;
 6   Rows   PLS_INTEGER := 10;
 7 BEGIN
 8   OPEN C1;
 9   -- Fetch 10 rows or less in each iteration
10   LOOP
11     FETCH C1 BULK COLLECT
12       INTO Empids LIMIT Rows;
13     EXIT WHEN Empids.Count = 0;
14     -- EXIT WHEN c1%NOTFOUND; -- incorrect, can omit some data
15     Dbms_Output.Put_Line('------- Results from Each Bulk Fetch --------');
16     FOR i IN 1 .. Empids.Count LOOP
17       Dbms_Output.Put_Line('Employee Id: ' || Empids(i));
18     END LOOP;
19   END LOOP;
20   CLOSE C1;
21 END;

使用

EXIT WHEN c1%NOTFOUND、使用该语句退出集合存在问题,因为他可能会忽略一些记录,
应该使用
EXIT WHEN Empids.Count = 0;来进行游标FOR循环的退出。
但是也可以在自己的业务处理完毕之后,使用
 EXIT WHEN c1%NOTFOUND;来退出集合

3、Implicit Fetch Calls BULK mode

1 BEGIN
2   FOR i IN (SELECT Descr
3               FROM Hardware
4              WHERE Aisle = 1
5                AND Item BETWEEN 1 AND 500) LOOP
6     NULL;
7   END LOOP;
8 END;

三种形式的集合:

            • Varray
            • Nested table
            • Associative array

   

-------------------------------------------------------------------------------------------------

     Getting Started with BULK Fetch

:FORALL的例子

 1 DECLARE
 2   TYPE t_Row_List IS TABLE OF Hardware%ROWTYPE;
 3   l_Row t_Row_List := t_Row_List();
 4 BEGIN
 5   FOR i IN 1 .. 100 LOOP
 6     l_Row.Extend;
 7     l_Row(i).Aisle := 1;
 8     l_Row(i).Item := i;
 9   END LOOP;
10   FORALL i IN 1 .. 100
11     INSERT INTO Hardware VALUES l_Row (i);
12 END;

批量处理的标准步骤:

   场景1:Elements Do Not Start at 1,序号没有从1开始,但是他还是连续的,可以使用.FIRST和.LAST作为循环的开始和结束:

 1 DECLARE
 2   TYPE t_Num_List IS TABLE OF Hardware.Item%TYPE INDEX BY PLS_INTEGER;
 3   Val t_Num_List;
 4 BEGIN
 5   Val(10) := 10;
 6   Val(11) := 20;
 7   Val(12) := 20;
 8   FORALL i IN Val.First .. Val.Last
 9     INSERT INTO Hardware (Item) VALUES (Val(i));
10 END;

场景2:Elements Are Not Contiguous,不联系的情况,可以使用Oracle提供的INDICES OF函数,

 1 DECLARE
 2   TYPE t_Num_List IS TABLE OF Hardware.Item%TYPE INDEX BY PLS_INTEGER;
 3   Val t_Num_List;
 4 BEGIN
 5   Val(10) := 10;
 6   -- val(11) := 20;
 7   Val(12) := 20;
 8   FORALL i IN INDICES OF Val
 9     INSERT INTO Hardware (Item) VALUES (Val(i));
10 END;

        I recommend adopting a standard of using the INDICES OF clause whenever you want to process an entire collection, and that use of .FIRST, .LAST, and .COUNT should be deprecated in your PL/SQL code. Sadly, the INDICES OF extension can only be used in a FORALL  statement, not in a standard FOR loop.(推荐在你的代码中使用INDICES OF来处理整个集合,FIRST,LAST,COUNT在你的PL/SQL编码不应该在出现,不幸的是,INDICES OF只可以用在FORALL自居中,不能使用在标准的LOOP循环中。)

VALUES OF使用:

 1 DECLARE
 2   TYPE t_Input_Row IS RECORD(
 3     Item   Hardware.Item%TYPE,
 4     Descr  Hardware.Descr%TYPE,
 5     Status VARCHAR2(3));
 6   TYPE t_Input_List IS TABLE OF t_Input_Row INDEX BY PLS_INTEGER;
 7   Src t_Input_List;
 8   TYPE t_Target_Indices IS TABLE OF PLS_INTEGER INDEX BY PLS_INTEGER;
 9   Ind_New t_Target_Indices;
10   Ind_Upd t_Target_Indices;
11 BEGIN
12   FOR i IN 1 .. 100 LOOP
13     Src(i).Item := i;
14     Src(i).Descr := 'Item ' || i;
15     Src(i).Status := CASE
16                        WHEN MOD(i, 5) = 0 THEN
17                         'NEW'
18                        ELSE
19                         'UPD'
20                      END;
21   END LOOP;
22   FOR i IN 1 .. 100 LOOP
23     IF Src(i).Status = 'NEW' THEN
24       Ind_New(Ind_New.Count) := i;
25     ELSE
26       Ind_Upd(Ind_Upd.Count) := i;
27     END IF;
28   END LOOP;
29   FORALL i IN VALUES OF Ind_New
30     INSERT INTO Hardware (Aisle, Item) VALUES (1, Src(i).Item);
31   Dbms_Output.Put_Line(SQL%ROWCOUNT || ' rows inserted');
32   FORALL i IN VALUES OF Ind_Upd
33     UPDATE Hardware
34        SET Descr = Src(i).Descr
35      WHERE Aisle = 1
36        AND Item = Src(i).Item;
37   Dbms_Output.Put_Line(SQL%ROWCOUNT || ' rows updated');
38 END;

--具体请参考:

<<Expert_PL_SQL_Practices_for_Oracle_Developers_and_DBAs>>:

Chapter 6: Bulk SQL Operations

原文地址:https://www.cnblogs.com/caroline/p/2529163.html