Oracle数据库的BULK COLLECT用法之批量增删改
2011-10-07 09:43
274 查看
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"的所有书籍检索出来并置于记录的一个关联数组中,它们都位于通向该数据库的单一通道中。
does notexist,对于FORALL的简单应用,这一规则不会引起任何麻烦。但是,如果想尽可能地充分利用FORALL,那么要求任意FORALL驱动数 组都要依次填充可能会增加程序的复杂性并降低性能。
在Oracle数据库10g中,PL/SQL现在在FORALL语句中提供了两个新子句:INDICES OF与VALUES OF,它们使你能够仔细选择驱动数组中该由扩展DML语句来处理的行。
当绑定数组为稀疏数组或者包含有间隙时,INDICES OF会非常有用。该语句的语法结构为:
假定我需要编写一个程序,对合格员工(由comp_analysis.is_eligible函数确定)加薪,编写关于不符合加薪条件的员工的报告 并写入employee_history表。我在一个非常大的公司工作;我们的员工非常非常多。对于一位PL/SQL开发人员来说,这并不是一项十分困难 的工作。我甚至不需要使用BULKCOLLECT或FORALL就可以完成这项工作,如清单1所示,我使用一个CURSORFOR循环和单独的 INSERT及UPDATE语句。这样的代码简洁明了;不幸地是,我花了10分钟来运行此代码,我的"老式"方法要运行30分钟或更长时间。
清单 1:
清单 2:
定义集合类型与集合
在清单2中,声明段的第一部分(第6行至第11行)定义了几种不同的集合类型,与我将从员工表检索出的列相对应。我更喜欢基于employee% ROWTYPE来声明一个集合类型,但是FORALL还不支持对某些记录集合的操作,在这样的记录中,我将引用个别字段。所以,我还必须为员工ID、薪金 和雇用日期分别声明其各自的集合。
接下来为每一列声明所需的集合(第13行至第21行)。首先定义与所查询列相对应的集合(第13行至第15行):
最后,我再为每一列声明一个集合(第19行至第21行),用于记录没有加薪资格的员工:
用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"的所有书籍检索出来并置于记录的一个关联数组中,它们都位于通向该数据库的单一通道中。
DECLARE TYPE books_aat IS TABLE OF book%ROWTYPE INDEX BY PLS_INTEGER; books books_aat; BEGIN SELECT * BULK COLLECT INTO book FROM books WHERE title LIKE '%PL/SQL%'; ... END;类似地,FORALL将数据从一个PL/SQL集合传送给指定的使用集合的表。下面的代码实例给出一个过程,即接收书籍信息的一个嵌套表,并将该集 合(绑定数组)的全部内容插入该书籍表中。注意,这个例子还利用了Oracle9i的FORALL的增强功能,可以将一条记录直接插入到表中。BULK COLLECT和FORALL都非常有用,它们不仅提高了性能,而且还简化了为PL/SQL中的SQL操作所编写的代码。下面的多行FORALL INSERT相当清楚地说明了为什么PL/SQL被认为是Oracle数据库的最佳编程语言。
CREATE TYPE books_nt IS TABLE OF book%ROWTYPE; / CREATE OR REPLACE PROCEDURE add_books ( books_in IN books_nt) IS BEGIN FORALL book_index IN books_in.FIRST .. books_in.LAST INSERT INTO book VALUES books_in(book_index); ... END;不过在Oracle数据库10g之前,以FORAll方式使用集合有一个重要的限制:该数据库从IN范围子句中的第一行到最后一行,依次读取集合的 内容。如果在该范围内遇到一个未定义的行,Oracle数据库将引发ORA-22160异常事件:ORA-22160: element at index
does notexist,对于FORALL的简单应用,这一规则不会引起任何麻烦。但是,如果想尽可能地充分利用FORALL,那么要求任意FORALL驱动数 组都要依次填充可能会增加程序的复杂性并降低性能。
在Oracle数据库10g中,PL/SQL现在在FORALL语句中提供了两个新子句:INDICES OF与VALUES OF,它们使你能够仔细选择驱动数组中该由扩展DML语句来处理的行。
当绑定数组为稀疏数组或者包含有间隙时,INDICES OF会非常有用。该语句的语法结构为:
FORALL indx IN INDICES OF sparse_collection INSERT INTO my_table VALUES sparse_collection (indx);VALUES OF用于一种不同的情况:绑定数组可以是稀疏数组,也可以不是,但我只想使用该数组中元素的一个子集。那么我就可以使用VALUES OF来指向我希望在DML操作中使用的值。该语句的语法结构为:
FORALL indx IN VALUES OF pointer_array INSERT INTO my_table VALUES binding_array (indx);不用FOR循环而改用FORALL
假定我需要编写一个程序,对合格员工(由comp_analysis.is_eligible函数确定)加薪,编写关于不符合加薪条件的员工的报告 并写入employee_history表。我在一个非常大的公司工作;我们的员工非常非常多。对于一位PL/SQL开发人员来说,这并不是一项十分困难 的工作。我甚至不需要使用BULKCOLLECT或FORALL就可以完成这项工作,如清单1所示,我使用一个CURSORFOR循环和单独的 INSERT及UPDATE语句。这样的代码简洁明了;不幸地是,我花了10分钟来运行此代码,我的"老式"方法要运行30分钟或更长时间。
清单 1:
CREATE OR REPLACE PROCEDUREgive_raises_in_department ( dept_in IN employee.department_id%TYPE , newsal IN employee.salary%TYPE ) IS CURSOR emp_cur IS SELECT employee_id, salary, hire_date FROM employee WHERE department_id = dept_in; BEGIN FOR emp_rec IN emp_cur LOOP IF comp_analysis.is_eligible (emp_rec.employee_id) THEN UPDATE employee SET salary = newsal WHERE employee_id =emp_rec.employee_id; ELSE INSERT INTO employee_history (employee_id, salary , hire_date, activity ) VALUES (emp_rec.employee_id,emp_rec.salary , emp_rec.hire_date,'RAISE DENIED' ); END IF; END LOOP; END give_raises_in_department;好在我公司的数据库升级到了Oracle9i,而且更幸运的是,在最近的Oracle研讨会上(以及Oracle技术网站提供的非常不错的演示中)我了解到了批量处理方法。所以我决定使用集合与批量处理方法重新编写程序。写好的程序如清单2所示。
清单 2:
CREATE OR REPLACE PROCEDUREgive_raises_in_department ( dept_in IN employee.department_id%TYPE ,newsal IN employee.salary%TYPE ) IS TYPE employee_aat IS TABLE OF employee.employee_id%TYPE INDEX BY PLS_INTEGER; TYPE salary_aat IS TABLE OF employee.salary%TYPE INDEX BY PLS_INTEGER; TYPE hire_date_aat IS TABLE OF employee.hire_date%TYPE INDEX BY PLS_INTEGER; employee_ids employee_aat; salaries salary_aat; hire_dates hire_date_aat; approved_employee_ids employee_aat; denied_employee_ids employee_aat; denied_salaries salary_aat; denied_hire_dates hire_date_aat; PROCEDURE retrieve_employee_info IS BEGIN SELECT employee_id, salary, hire_date BULK COLLECT INTO employee_ids, salaries, hire_dates FROM employee WHERE department_id = dept_in; END; PROCEDURE partition_by_eligibility IS BEGIN FOR indx IN employee_ids.FIRST .. employee_ids.LAST LOOP IF comp_analysis.is_eligible (employee_ids (indx)) THEN approved_employee_ids (indx) :=employee_ids (indx); ELSE denied_employee_ids (indx) :=employee_ids (indx); denied_salaries (indx) :=salaries (indx); denied_hire_dates (indx) :=hire_dates (indx); END IF; END LOOP; END; PROCEDURE add_to_history IS BEGIN FORALL indx IN denied_employee_ids.FIRST .. denied_employee_ids.LAST INSERT INTO employee_history (employee_id , salary , hire_date, activity ) VALUES (denied_employee_ids(indx) , denied_salaries (indx) , denied_hire_dates(indx), 'RAISE DENIED' ); END; PROCEDURE give_the_raise IS BEGIN FORALL indx IN approved_employee_ids.FIRST .. approved_employee_ids.LAST UPDATE employee SET salary = newsal WHERE employee_id =approved_employee_ids (indx); END; BEGIN retrieve_employee_info; partition_by_eligibility; add_to_history; give_the_raise; END give_raises_in_department;扫一眼清单1 和清单2 就会清楚地认识到:改用集合和批量处理方法将增加代码量和复杂性。但是,如果你需要大幅度提升性能,这还是值得的。下面,我们不看这些代码,我们来看一看当使用FORALL时,用什么来处理CURSORFOR循环内的条件逻辑。
定义集合类型与集合
在清单2中,声明段的第一部分(第6行至第11行)定义了几种不同的集合类型,与我将从员工表检索出的列相对应。我更喜欢基于employee% ROWTYPE来声明一个集合类型,但是FORALL还不支持对某些记录集合的操作,在这样的记录中,我将引用个别字段。所以,我还必须为员工ID、薪金 和雇用日期分别声明其各自的集合。
接下来为每一列声明所需的集合(第13行至第21行)。首先定义与所查询列相对应的集合(第13行至第15行):
employee_ids employee_aat; salaries salary_aat; hire_dates hire_date_aat;然后我需要一个新的集合,用于存放已被批准加薪的员工的ID(第17行):approved_employee_ids employee_aat;
最后,我再为每一列声明一个集合(第19行至第21行),用于记录没有加薪资格的员工:
denied_employee_ids employee_aat; denied_salaries salary_aat; denied_hire_dates hire_date_aat;关于Oracle数据库的bulk collect用法之批量增删改的相关操作就介绍到这里了,希望本次的介绍能够对您有所收获!
相关文章推荐
- Oracle 的 bulk collect用法——批量增删改
- Oracle 的 FORALL用法(批量增删改)
- Oracle 的 FORALL用法(批量增删改)
- Oracle 的 bulk collect用法——批量查询
- mybatis中Oracle数据库的批量增删改操作小结
- oracle数据库定时任务dbms_job的用法详解
- bulk collect用法小结
- Oracle批量SQL之 BULK COLLECT 子句
- 如何将Excel表格中的数据批量导入到Oracle数据库表中
- 批量增删改
- OracleBulkCopy 批量插入oracle数据库的方法
- 【转】Oracle数据库中Sequence的用法
- 关于ntext的批量修改 这个比较总要的就是一些函数的用法
- bulk collect用法小结
- Oracle数据库在批量插入数据时提示 ORA-01653: 表 EP.EP*** 无法通过 128 (在表空间 EP 中) 扩展
- Java:批量插入、修改数据到数据库中的用法
- 批量增删改数据
- oracle中bulk collect用法
- oracle数据库的sequence序列号的用法
- 讲解Oracle数据库中的数据字典及相关SQL查询用法