Oracle数据库的BULK COLLECT用法之批量增删改

Oracle数据库的BULK COLLECT用法之批量增删改的相关知识是本文我们主要要介绍的内容,FORALL语句的一个关键性改进,它可以大大简化代码,并且对于那些要在PL/SQL程序中更新很多行数据的程序来说,它可显著提高其性能。

用FORALL来增强DML的处理能力

Oracle为Oracle8i中的PL/SQL引入了两个新的数据操纵语言(DML)语句:BULK COLLECT和FORALL。这两个语句在PL/SQL内部进行一种数组处理;BULK COLLECT提供对数据的高速检索,FORALL可大大改进INSERT、UPDATE和DELETE操作的性能。Oracle数据库使用这些语句大大减少了。

PL/SQL与SQL语句执行引擎的环境切换次数,从而使其性能有了显著提高。使用BULK COLLECT,你可以将多个行引入一个或多个集合中,而不是单独变量或记录中。下面这个BULK COLLECT的实例是将标题中包含有”PL/SQL”的所有书籍检索出来并置于记录的一个关联数组中,它们都位于通向该数据库的单一通道中。

 

  1. DECLARE  
  2. TYPE books_aat  
  3. IS TABLE OF book%ROWTYPE  
  4. INDEX BY PLS_INTEGER;  
  5. books books_aat;  
  6. BEGIN  
  7. SELECT *  
  8. BULK COLLECT INTO book  
  9. FROM books  
  10. WHERE title LIKE '%PL/SQL%';  
  11. ...  
  12. END; 

 

类似地,FORALL将数据从一个PL/SQL集合传送给指定的使用集合的表。下面的代码实例给出一个过程,即接收书籍信息的一个嵌套表,并将该集合(绑定数组)的全部内容插入该书籍表中。注意,这个例子还利用了Oracle9i的FORALL的增强功能,可以将一条记录直接插入到表中。BULK COLLECT和FORALL都非常有用,它们不仅提高了性能,而且还简化了为PL/SQL中的SQL操作所编写的代码。下面的多行FORALL INSERT相当清楚地说明了为什么PL/SQL被认为是Oracle数据库的***编程语言。

 

  1. CREATE TYPE books_nt  
  2. IS TABLE OF book%ROWTYPE;  
  3. /  
  4. CREATE OR REPLACE PROCEDURE add_books (  
  5. books_in IN books_nt)  
  6. IS  
  7. BEGIN  
  8. FORALL book_index  
  9. IN books_in.FIRST .. books_in.LAST  
  10. INSERT INTO book  
  11. VALUES books_in(book_index);  
  12. ...  
  13. END; 

 

不过在Oracle数据库10g之前,以FORAll方式使用集合有一个重要的限制:该数据库从IN范围子句中的***行到***一行,依次读取集合的内容。如果在该范围内遇到一个未定义的行,Oracle数据库将引发ORA-22160异常事件:ORA-22160: element at index [N] does notexist,对于FORALL的简单应用,这一规则不会引起任何麻烦。但是,如果想尽可能地充分利用FORALL,那么要求任意FORALL驱动数组都要依次填充可能会增加程序的复杂性并降低性能。

在Oracle数据库10g中,PL/SQL现在在FORALL语句中提供了两个新子句:INDICES OF与VALUES OF,它们使你能够仔细选择驱动数组中该由扩展DML语句来处理的行。

当绑定数组为稀疏数组或者包含有间隙时,INDICES OF会非常有用。该语句的语法结构为:

 

  1. FORALL indx IN INDICES  
  2. OF sparse_collection  
  3. INSERT INTO my_table  
  4. VALUES sparse_collection (indx); 

 

VALUES OF用于一种不同的情况:绑定数组可以是稀疏数组,也可以不是,但我只想使用该数组中元素的一个子集。那么我就可以使用VALUES OF来指向我希望在DML操作中使用的值。该语句的语法结构为:

 

  1. FORALL indx IN VALUES OF pointer_array  
  2. INSERT INTO my_table  
  3. VALUES binding_array (indx); 

 

不用FOR循环而改用FORALL

假定我需要编写一个程序,对合格员工(由comp_analysis.is_eligible函数确定)加薪,编写关于不符合加薪条件的员工的报告并写入employee_history表。我在一个非常大的公司工作;我们的员工非常非常多。对于一位PL/SQL开发人员来说,这并不是一项十分困难的工作。我甚至不需要使用BULKCOLLECT或FORALL就可以完成这项工作,如清单1所示,我使用一个CURSORFOR循环和单独的INSERT及UPDATE语句。这样的代码简洁明了;不幸地是,我花了10分钟来运行此代码,我的”老式”方法要运行30分钟或更长时间。

清单 1:

 

  1. CREATE OR REPLACE PROCEDUREgive_raises_in_department (  
  2. dept_in IN employee.department_id%TYPE  
  3. , newsal IN employee.salary%TYPE  
  4. )  
  5. IS  
  6. CURSOR emp_cur  
  7. IS  
  8. SELECT employee_id, salary, hire_date  
  9. FROM employee  
  10. WHERE department_id = dept_in;  
  11. BEGIN  
  12. FOR emp_rec IN emp_cur  
  13. LOOP  
  14. IF comp_analysis.is_eligible (emp_rec.employee_id)  
  15. THEN  
  16. UPDATE employee  
  17. SET salary = newsal 
  18. WHERE employee_id =emp_rec.employee_id;  
  19. ELSE  
  20. INSERT INTO employee_history  
  21. (employee_id, salary  
  22. , hire_date, activity  
  23. )  
  24. VALUES (emp_rec.employee_id,emp_rec.salary  
  25. , emp_rec.hire_date,'RAISE DENIED'  
  26. );  
  27. END IF;  
  28. END LOOP;  
  29. END give_raises_in_department; 

 

好在我公司的数据库升级到了Oracle9i,而且更幸运的是,在最近的Oracle研讨会上(以及Oracle技术网站提供的非常不错的演示中)我了解到了批量处理方法。所以我决定使用集合与批量处理方法重新编写程序。写好的程序如清单2所示。

清单 2:

  1. CREATE OR REPLACE PROCEDUREgive_raises_in_department (  
  2. dept_in IN employee.department_id%TYPE  
  3. ,newsal IN employee.salary%TYPE  
  4.  )  
  5. IS  
  6. TYPE employee_aat IS TABLE OF employee.employee_id%TYPE  
  7. INDEX BY PLS_INTEGER;  
  8. TYPE salary_aat IS TABLE OF employee.salary%TYPE  
  9. INDEX BY PLS_INTEGER;  
  10. TYPE hire_date_aat IS TABLE OF employee.hire_date%TYPE  
  11. INDEX BY PLS_INTEGER;  
  12. employee_ids employee_aat;  
  13. salaries salary_aat;  
  14. hire_dates hire_date_aat;  
  15. approved_employee_ids employee_aat;  
  16. denied_employee_ids employee_aat;  
  17. denied_salaries salary_aat;  
  18. denied_hire_dates hire_date_aat;  
  19. PROCEDURE retrieve_employee_info  
  20. IS  
  21. BEGIN  
  22. SELECT employee_id, salary, hire_date  
  23. BULK COLLECT INTO employee_ids, salaries, hire_dates  
  24. FROM employee  
  25. WHERE department_id = dept_in;  
  26. END;  
  27. PROCEDURE partition_by_eligibility  
  28. IS  
  29. BEGIN  
  30. FOR indx IN employee_ids.FIRST .. employee_ids.LAST  
  31. LOOP  
  32. IF comp_analysis.is_eligible (employee_ids (indx))  
  33. THEN  
  34. approved_employee_ids (indx) :=employee_ids (indx);  
  35. ELSE  
  36. denied_employee_ids (indx) :=employee_ids (indx);  
  37. denied_salaries (indx) :=salaries (indx);  
  38. denied_hire_dates (indx) :=hire_dates (indx);  
  39. END IF;  
  40. END LOOP;  
  41. END;  
  42. PROCEDURE add_to_history  
  43. IS  
  44. BEGIN  
  45. FORALL indx IN denied_employee_ids.FIRST .. denied_employee_ids.LAST  
  46. INSERT INTO employee_history  
  47. (employee_id  
  48. , salary  
  49. , hire_date, activity  
  50. )  
  51. VALUES (denied_employee_ids(indx)  
  52. , denied_salaries (indx)  
  53. , denied_hire_dates(indx), 'RAISE DENIED'  
  54.  );  
  55. END;  
  56. PROCEDURE give_the_raise  
  57. IS  
  58. BEGIN  
  59. FORALL indx IN approved_employee_ids.FIRST .. approved_employee_ids.LAST  
  60. UPDATE employee  
  61. SET salary = newsal 
  62. WHERE employee_id =approved_employee_ids (indx);  
  63. END;  
  64. BEGIN  
  65. retrieve_employee_info;  
  66. partition_by_eligibility;  
  67. add_to_history;  
  68. give_the_raise;  
  69. END give_raises_in_department; 

 

扫一眼清单1 和清单2 就会清楚地认识到:改用集合和批量处理方法将增加代码量和复杂性。但是,如果你需要大幅度提升性能,这还是值得的。下面,我们不看这些代码,我们来看一看当使用FORALL时,用什么来处理CURSORFOR循环内的条件逻辑。

定义集合类型与集合

在清单2中,声明段的***部分(第6行至第11行)定义了几种不同的集合类型,与我将从员工表检索出的列相对应。我更喜欢基于employee% ROWTYPE来声明一个集合类型,但是FORALL还不支持对某些记录集合的操作,在这样的记录中,我将引用个别字段。所以,我还必须为员工ID、薪金和雇用日期分别声明其各自的集合。

接下来为每一列声明所需的集合(第13行至第21行)。首先定义与所查询列相对应的集合(第13行至第15行):

 

  1. employee_ids employee_aat;  
  2. salaries salary_aat;  
  3. hire_dates hire_date_aat; 

 

然后我需要一个新的集合,用于存放已被批准加薪的员工的ID(第17行):approved_employee_ids employee_aat;

***,我再为每一列声明一个集合(第19行至第21行),用于记录没有加薪资格的员工:

 

  1. denied_employee_ids employee_aat;  
  2. denied_salaries salary_aat;  
  3. denied_hire_dates hire_date_aat; 

 

关于Oracle数据库的bulk collect用法之批量增删改的相关操作就介绍到这里了,希望本次的介绍能够对您有所收获!

【编辑推荐】

  1. Oracle学习笔记之DECODE及常用窗口函数
  2. Oracle数据库各类控制语句的使用详细介绍
  3. Oracle数据库日期范围查询的两种实现方式
  4. Oracle数据库只读模式的CACHE BUFFERS CHAINS测试
  5. Oracle 10g数据库中UNDO_RETENTION参数的使用详解

文章来源网络,作者:管理,如若转载,请注明出处:https://shuyeidc.com/wp/239522.html<

(0)
管理的头像管理
上一篇2025-04-23 11:00
下一篇 2025-04-23 11:02

相关推荐

  • jsp空间购买和交换数据空间怎么买,有哪些注意事项?

    购买JSP空间时,是否考虑过数据交换空间的性能?简米科技(2003年始创,23年行业沉淀)与酷番云(工信部一类增值电信全牌照)这类持牌自营机房的服务商,能确保数据交换的高效稳定,是值得优先选择的合作伙伴,为什么JSP空间需要搭配独立的数据交换空间从JSP应用特性看数据交换需求JSP基于Java技术,常用于企业级……

    2026-08-11
    0
  • 建网站用香港空间效果怎么样,香港空间稳定吗?

    建网站用香港空间,对于创建网站资产来说,核心价值在于免备案和全球带宽优势,尤其适合外贸、跨境电商和需要快速启动的项目,但你必须权衡国内访问延迟,并选择有资质的服务商以保证资产安全,香港空间的核心优势与适用边界免备案:节省时间就是节省成本国内服务器需要备案,通常需要10到20天,香港空间无需备案,域名解析后即可上……

    2026-08-11
    0
  • Java连接云数据库的方法是什么,如何操作

    Java连接云数据库的核心在于通过JDBC驱动,结合云服务商提供的连接地址、端口、数据库名及认证信息,配置安全策略(如SSL、IP白名单),即可实现稳定高效的远程数据库访问,基础准备:JDBC驱动与依赖管理连接云数据库前,需要确保开发环境具备对应的JDBC驱动,以最常见的MySQL为例,你需要引入mysql-c……

    2026-08-11
    0
  • 建网站公安联网备案必须使用数据码吗,备案流程是什么

    网站备案包括ICP备案和公安联网备案,两者缺一不可,公安联网备案必须使用服务商提供的数据码,选择持有合法资质的服务商是顺利通过备案的前提,为什么网站必须进行公安联网备案根据公安部《计算机信息网络国际联网安全保护管理办法》,网站开通后30日内必须到公安机关办理备案手续,未完成公安备案的网站,面临责令整改、关闭网站……

    2026-08-10
    0
  • 建一个企业网站大概需要多少钱?,怎么收费?

    建网站要多少钱,没有一个固定的数字,几百到几万都可能,但真正的“创建网站资产”绝不仅仅是初次投入的成本,而是基于长期稳定、合规和安全的持续性投入,其中核心取决于你选择了什么样的“地基”来承载你的业务,建站预算的构成与行业基准当你开始规划一个网站,最先面对的就是预算问题,一个常见的误区是只关注网站“看起来”的建造……

    2026-08-10
    0

发表回复

您的邮箱地址不会被公开。必填项已用 * 标注