【问题标题】:Using FORALL in Oracle with Update and insert在 Oracle 中使用 FORALL 和更新和插入
【发布时间】:2021-11-25 02:32:21
【问题描述】:

我是 PL/SQL 的新手。 我有一个类似的程序:

 create or replace procedure insert_charge is
   v_count       number;
 begin
 
   for i in (select t.name, t.hire_date, t.salary
               from emp t
              where t.create_date >= (sysdate - 30)
                and t.salary = 0) loop
 
       insert into charge
         (name, hire_date, salary)
       values
         (i.name, hire_date, salary);
       commit;
     
       update emp l
          set l.status = 1
        where l.name = i.name
          and l.status = 0
          and l.hire_date = i.hire_date;
       commit;
   end loop;
 exception
   when others then
     rollback;
 end insert_charge;

如何使用 FORALL 语句代替这个?

【问题讨论】:

  • 很高兴您开始进行批量处理,您应该会看到性能有一些显着改善。查看 Oracle 的本教程,看看它是否回答了您的问题:(Oracle Paper)
  • 您可以将bulk collect 的所有数据放入一个集合中,然后对insert` 和update 应用两个forall 语句。使用一些示例数据来演示一个工作示例会更容易。

标签: sql oracle plsql


【解决方案1】:

FORALL 还有一些额外的任务;即定义一个集合来定义大容量区域和一个该集合类型的变量来包含实际数据。作为安全值,您应该对一次获取的数量设置一个限制。 Bulk Collect/Forall 是速度与内存的权衡。并且在某个点(取决于您的配置)收益递减。除了您用于它的内存之外,数据库中的其他进程无法使用。很好地解决您的其他问题。然后正如@Littlefoot 指出的那样,不要挤压异常记录它们并重新加注。最后,关于提交的注释。 **不要在每个 DML 语句之后提交,您可能需要花一些时间调查 [transactions][1]。考虑到这一点,您的程序将变为:

create or replace procedure insert_charge is
     cursor c_emp_cur is 
            select t.name, t.hire_date, t.salary
               from emp t
              where t.create_date >= (sysdate - 30)
                and t.salary = 0; 
                
     type c_emp_array_t is table of c_emp%rowtype ;  -- define collection for rows selected
     
     k_emp_rows_max constant integer := 500;         -- defines the maximum rows per fetch
     l_emp_list     c_emp_array_t;                   -- define variable of rows collection 
 begin
    open c_emp_cur; 
       
    loop 
        fetch c_emp_cur                              -- fetch up to LIMIT rows from cursor
         bulk collect 
         into l_emp_collect
        limit k_emp_rows_max; 
        
        forall i in 1 .. l_emp_collect.count         -- run insert for ALL rows in the collection
            insert into charge(name, hire_date, salary)
                 values( l_emp_collect(i).name 
                       , l_emp_collect(i).hire_date
                       , l_emp_collect(i).salary);
                       
        forall i in 1 .. l_emp_collect.count        -- run update for ALL rows in the collection          
            update emp l
               set l.status = 1
             where l.name = l_emp_collect(i).name
               and l.status = 0
               and l.hire_date = l_emp_collect(i).hire_date;
            
    exit when c_emp_cur%notfound;                     -- no more rows so exit
    end loop;
    
    close c_emp_cur; 
    commit;                                          -- JUST 1 COMMIT; 
 exception
   when others then
        generate_exception_log ('insert_charge', sysdate, sql_errm );    --ASSUMED Anonomous Transaction procedure for exception log table. 
        raise;
 end insert_charge;      

免责声明:未经测试。 [1]:https://www.techopedia.com/definition/16455/transaction-databases

【讨论】:

    【解决方案2】:

    你不能。

    FORALL 语句运行一个 DML 语句多次

    ONE DML 语句。您有两个(更新和插入)。


    截至您编写的代码:

    • COMMIT 移出循环
    • 删除when others“handler”,因为它什么都不处理。如果发生错误,Oracle 将静默回滚并报告过程已成功完成,而它 - 实际上 - 失败了

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-08-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-13
      相关资源
      最近更新 更多