FORALL语句的一个关键性改进,它可大大简化代码,并且对于那些要在PL/SQL程序中更新很多行数据的程序来说,它可显着提高其性能。
1:用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 [N] does not exist
对于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开发人员来说,这并不是一项十分困难的工作。我甚至不需要使用BULK
COLLECT或FORALL就可以完成这项工作,如清单 1所示,我使用一个CURSOR
FOR循环和单独的INSERT及UPDATE语句。这样的代码简洁明了;不幸地是,我花了10分钟来运行此代码,我的"老式"方法要运行30分钟或更长
时间。
清单 1:
CREATE OR REPLACE PROCEDURE give_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:
1 CREATE OR REPLACE PROCEDURE give_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
13 employee_ids employee_aat;
14 salaries salary_aat;
15 hire_dates hire_date_aat;
16
17 approved_employee_ids employee_aat;
18
19 denied_employee_ids employee_aat;
20 denied_salaries salary_aat;
21 denied_hire_dates hire_date_aat;
22
23 PROCEDURE retrieve_employee_info
24 IS
25 BEGIN
26 SELECT employee_id, salary, hire_date
27 BULK COLLECT INTO employee_ids, salaries, hire_dates
28 FROM employee
29 WHERE department_id = dept_in;
30 END;
31
32 PROCEDURE partition_by_eligibility
33 IS
34 BEGIN
35 FOR indx IN employee_ids.FIRST .. employee_ids.LAST
36 LOOP
37 IF comp_analysis.is_eligible (employee_ids (indx))
38 THEN
39 approved_employee_ids (indx) := employee_ids (indx);
40 ELSE
41 denied_employee_ids (indx) := employee_ids (indx);
42 denied_salaries (indx) := salaries (indx);
43 denied_hire_dates (indx) := hire_dates (indx);
44 END IF;
45 END LOOP;
46 END;
47
48 PROCEDURE add_to_history
49 IS
50 BEGIN
51 FORALL indx IN denied_employee_ids.FIRST .. denied_employee_ids.LAST
52 INSERT INTO employee_history
53 (employee_id
54 , salary
55 , hire_date, activity
56 )
57 VALUES (denied_employee_ids (indx)
58 , denied_salaries (indx)
59 , denied_hire_dates (indx), 'RAISE DENIED'
60 );
61 END;
62
63 PROCEDURE give_the_raise
64 IS
65 BEGIN
66 FORALL indx IN approved_employee_ids.FIRST .. approved_employee_ids.LAST
67 UPDATE employee
68 SET salary = newsal
69 WHERE employee_id = approved_employee_ids (indx);
70 END;
71 BEGIN
72 retrieve_employee_info;
73 partition_by_eligibility;
74 add_to_history;
75 give_the_raise;
76 END give_raises_in_department;
扫一眼清单1 和清单2 就会清楚地认识到:改用集合和批量处理方法将增加代码量和复杂性。但是,如果你需要大幅度提升性能,这还是值得的。下面,我们不看这些代码,我们来看一看当使用FORALL时,用什么来处理CURSOR FOR循环内的条件逻辑。
定义集合类型与集合
在清单
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;
深入了解代码
数据结构确定后,我们现在跳过该程序的执行部分(第72行至第75行),了解如何使用这些集合来加速进程。
retrieve_employee_info;
partition_by_eligibility;
add_to_history;
give_the_raise;
我编写此程序使用了逐步细化法(也被称为"自顶向下设计")。所以执行部分不是很长,也不难理解,只有四行,按名称对过程中的每一步进行了描
述。首先检索员工信息(指定部门的所有员工)。然后进行划分,将要加薪和不予加薪的员工区分出来。完成之后,我就可以将那些不予加薪的员工添加至员工历史
表中,对其他员工进行加薪。
以这种方式编写代码使最终结果的可读性大大增强。因而我可以深入到该程序中对我有意义的任何部分。
有了已声明的集合,我现在就可以使用BULK COLLECT来检索员工信息(第23行至第30行)。这一部分有效地替代了CURSOR FOR循环。至此,数据被加载到集合中。
划分逻辑(第32行至第46行)要求对刚刚填充的集合中的每一行进行检查,看其是否符合加薪条件。如果符合,我就将该员工ID从查询填充的集合
复制到符合条件的员工的集合。如果不符合,则复制该员工ID、薪金和雇用日期,因为这些都需要插入到employee_history表中。
初始数据现在已被分为两个集合,可以将其分别用作两个不同的FORALL语句(分别从第51行和第66行开始)的驱动器。我将不合格员工的集合
中的数据批量插入到employee_history(add_to_history)表中,并通过give_the_raise过程,在
employee表中批量更新合格员工的信息。
最后再仔细地看一看add_to_history(第48行至第61行),以此来结束对这个重新编写的程序的分析。FORALL语句(第51
行)包含一个IN子句,它指定了要用于批量INSERT的行号范围。在对程序进行第二次重写的说明中,我将把用于定义范围的集合称为"驱动集合"。但在
add_to_history的这一版本中,我简单地假定:使用在denied_employee_ids中定义的所有行。在INSERT自身内部,关于
不合格员工的三个集合都会被用到;我将把这些集合称为"数据集合"。可以看到,驱动集合与数据集合无需匹配。在学习Oracle数据库10g的新特性时,
这是一个关键点。
结果,清单 2 的行数大约是清单 1行数的2倍,但是清单 2 中的代码会在要求的时间内运行。在使用Oracle数据库10g之前,在这种情况下,我只会对能够在这一时间内运行代码并开始下一个任务这一点感到高兴。
不过,有了Oracle数据库10g中最新版的PL/SQL,现在我就可以在性能、可读性和代码量方面作出更多的改进。
将VALUES OF用于此过程在Oracle数据库10g中,可以指定FORALL语句使用的驱动集合中的行的子集。可以使用以下两种方法之一来定义该子集:
将数据集合中的行号与驱动集合中的行号进行匹配。你需要使用INDICES OF子句。
将数据集合中的行号与驱动集合中所定义行中找到的值进行匹配。这需要使用VALUES OF子句。
在对give_raises_in_department进行第二次和最后一次改写中我将使用VALUES OF子句。清单 3 包含这个版本的全部代码。我将略过这一程序中与前一版本相同的部分。
从声明集合开始,请注意我不再另外定义集合来存放合格的和不合格的员工信息,而是在清单 3 (第17行至第21行)中声明两个"引导"集合:
一个用于符合加薪要求的员工,另一个用于不符合加薪要求的员工。这两个集合的数据类型都是布尔型;不久将会看到,这些集合的数据类型与
FORALL语句毫无关系。FORALL语句只关心定义了哪些行。 在员工表中拥有50
000行信息的give_raises_in_department的三种执行方法的占用时间
执行方法 用时
CURSOR FOR循环 00:00:38.01
Oracle数据库10g之前的批量处理 00:00:06.09
Oracle数据库10g的批量处理 00:00:02.06
在员工表中拥有100,000行数据的give_raises_in_department的三种执行方法的占用时间 执行方法用时
CURSOR FOR循环 00:00:58.01
Oracle数据库10g之前的批量处理 00:00:12.00
Oracle数据库10g的批量处理 00:00:05.05
表1:处理50,000行和100,000行数据的用时测试结果
retrieve_employee_info子程序与前面的相同,但是对数据进行划分的方式完全不同(第32行至第44行)。我没有将记录从
一个集合复制到另一个集合(这个操作相对较慢),而只是确定与员工ID集合中的行号相匹配的相应引导集合中的行(通过为其指定一个TRUE值)。
现在可以在两个不同FORALL语句(由第49行和第65行开始)中,将approved_list和denied_list集合用作驱动集合。
为了插入到employee_history表中,我使用了如下语句:
FORALL indx IN VALUES OF denied_list
为了进行更新(给员工进行加薪),我使用这一格式:
FORALL indx IN VALUES OF approved_list
在这两个DML语句中,数据集合是在BULK COLLECT 检索步骤中填充的最初的集合;没有进行过复制。利用VALUES
OF,Oracle数据库在这些数据集合的行中进行筛选,仅使用行号与驱动集合中行号相匹配的行利用本程序中的VALUES
OF,可以避免复制对全部记录进行复制,而是用行号的一个简单列表来替换它们。对于大型数组,进行这些复制的开销是非常可观的。为了测试Oracle
数据库10g的优越性,我装入employee表并对50,000行和100,000行的数据运行测试。为了模拟更多的现实情况,我将Oracle数据库
10g之前的批量处理的执行方法作了修改以进行集合内容的多次复制。然后我使用SQL*Plus SET TIMING
ON来显示运行各个不同的执行方法所用的时间。表 1 给出了结果。
从这些时间测定得到的结论非常清楚:由单个DML语句变为批量处理将大幅缩短耗用时间,数据为50,000行时的用时由38秒减为6秒,数据为100,000行时的用时由58秒减为12秒。而且,通过使用VALUES OF来避免复制数据,我可以将用时缩短一半左右。
即使没有性能上的改进,VALUES OF及其同类子句--INDICES OF也提高了PL/SQL语言的灵活性,使开发人员能够更轻松地编写出更直观和更容易维护的代码。
在产品寿命这一点上,PL/SQL是一种成熟且功能强大的语言。因而,其很多新特性都是逐渐增加和改进而成的。不过,这些新特性还是使应用程序的性能和开发人员的开发效率有了重大改变。VALUES OF就是这种特性的一个很好的例子。
出自:http://www.examda.com/oracle/zonghe/20100604/092126955-3.html
分享到:
相关推荐
使用复合数据类型 8.1 PL/SQL记录 8.1.1 定义PL/SQL记录 8.1.2 使用PL/SQL记录 8.2 PL/SQL集合 8.2.1 索引表 ...8.3.1 FORALL语句 8.3.2 BULKCOLLECT子句 8.4 习题 第9章...
PL/SQL支持批量处理,通过诸如游标FOR循环和FORALL语句等机制,可以高效地处理大量数据。 **5、可移植性** PL/SQL代码可以在任何安装了Oracle数据库的平台上运行,具有良好的可移植性。 **6、与SQL紧密结合** PL...
除了在数据库中运行外,PL/SQL也可以在多种Oracle工具中使用,比如Oracle SQL Developer、Oracle GoldenGate等,这些工具提供了丰富的界面和功能支持PL/SQL开发。 ##### 三、PL/SQL的优势 **1、SQL的支持** PL/SQL...
内容概要:本文详细介绍了基于TMS320F系列芯片的C2000串口读写方案及其编程器——FlashPro2000的功能特点和支持的接口模式。文中不仅涵盖了硬件连接的具体步骤,还提供了代码实例来展示Flash擦除操作,并对比了JTAG和SCI-BOOT两种模式的优缺点。此外,针对不同型号的C2000系列芯片,给出了详细的适配指导以及避免烧录过程中可能出现的问题的方法。 适合人群:从事DSP开发的技术人员,尤其是对TI公司C2000系列芯片有一定了解并希望深入了解其编程和烧录细节的人群。 使用场景及目标:适用于实验室环境下的程序调试阶段,以及生产线上的批量烧录任务。主要目的是帮助开发者选择合适的编程工具和技术手段,提高工作效率,减少因误操作导致设备损坏的风险。 其他说明:文中提供的代码片段和命令行指令可以直接用于实际项目中,同时附带了一些实用技巧,如防止芯片变砖的小贴士和自动化重试脚本,有助于解决常见的烧录难题。
汉字字库存储芯片扩展实验 # 汉字字库存储芯片扩展实验 ## 实验目的 1. 了解汉字字库的存储原理和结构 2. 掌握存储芯片扩展技术 3. 学习如何通过硬件扩展实现大容量汉字字库存储 ## 实验原理 ### 汉字字库存储基础 - 汉字通常采用点阵方式存储(如16×16、24×24、32×32点阵) - 每个汉字需要占用32字节(16×16)到128字节(32×32)不等的存储空间 - 国标GB2312-80包含6763个汉字,需要较大存储容量 ### 存储芯片扩展方法 1. **位扩展**:增加数据总线宽度 2. **字扩展**:增加存储单元数量 3. **混合扩展**:同时进行位扩展和字扩展 ## 实验设备 - 单片机开发板(如STC89C52) - 存储芯片(如27C256、29C040等) - 逻辑门电路芯片(如74HC138、74HC373等) - 示波器、万用表等测试设备 - 连接线若干 ## 实验步骤 ### 1. 单芯片汉字存储实验 1. 连接27C256 EPROM芯片到单片机系统 2. 将16×16点阵汉字字库写入芯片 3. 编写程序读取并显示汉字 ### 2. 存储芯片字扩展实验 1. 使用地址译码器(如74HC138)扩展多片27C256 2. 将完整GB2312字库分布到各芯片中 3. 编写程序实现跨芯片汉字读取 ### 3. 存储芯片位扩展实验 1. 连接两片27C256实现16位数据总线扩展 2. 优化字库存储结构,提高读取速度 3. 测试并比较扩展前后的性能差异 ## 实验代码示例(单片机部分) ```c #include <reg52.h> #include <intrins.h> // 定义存储芯片控制引脚 sbit CE = P2^7; // 片选 sbit OE = P2^6; // 输出使能 sbit
测控装备干扰源快速侦测系统设计研究.pdf
嵌入式八股文面试题库资料知识宝典-【开发】嵌入式开源项目&库&资料.zip
嵌入式八股文面试题库资料知识宝典-百度2022年嵌入式面试题.zip
少儿编程scratch项目源代码文件案例素材-空间站.zip
基于关联规则的商业银行个性化产品推荐.pdf
嵌入式八股文面试题库资料知识宝典-Linux基础使用.zip
内容概要:本文详细介绍了利用MATLAB进行轴棱锥生成贝塞尔高斯光束及环形光束光强图像的仿真研究。首先阐述了实验的背景与目标,强调了MATLAB在光学和计算科学领域的广泛应用。接着,具体描述了实验的方法与步骤,包括材料准备、仿真过程中的参数设定和光束生成代码编写。最后,对实验结果进行了深入分析,展示了贝塞尔高斯光束和环形光束的光强分布特点,验证了其光学性能的预期表现。文章还对未来的研究方向和技术改进提出了展望。 适合人群:从事光学、物理学及相关领域研究的专业人士,特别是对光束生成和光学性能分析感兴趣的科研工作者。 使用场景及目标:适用于需要进行光束生成和性能分析的实验室环境,旨在帮助研究人员更好地理解和优化光束特性和传播行为。 其他说明:本文不仅提供了详细的实验方法和步骤,还附有丰富的实验结果和数据分析,为后续研究提供了宝贵的参考资料。
内容概要:本文探讨了三电平NPC型有源电力滤波器(APF)的模型预测控制(MPC)中存在的开关频率过高问题及其解决方案。传统MPC方法会导致极高的开关频率,增加了系统的能耗和热量。通过引入滞环控制模块,可以在不大幅牺牲性能的情况下有效降低开关频率。具体来说,滞环控制通过在价值函数计算后增加一个判断条件,对状态切换进行惩罚,从而减少不必要的开关动作。实验结果显示,开关频率从4392Hz降至3242Hz,降幅达26.2%,虽然电流总谐波畸变率(THD)略有上升,但仍符合国家标准。此外,文中还提出了动态调整滞环宽度的方法,以进一步优化不同负载条件下的表现。 适合人群:从事电力电子、电力系统控制领域的研究人员和技术人员,特别是关注APF和MPC技术的人群。 使用场景及目标:适用于需要优化APF系统开关频率的研究和工程项目,旨在提高系统效率并降低成本。目标是在不影响系统性能的前提下,显著降低开关频率,减少能量损失和热管理难度。 其他说明:文章不仅提供了理论分析,还包括具体的实现代码片段,有助于读者理解和实践。同时,强调了在实际应用中需要注意的问题,如中点电位漂移等。
内容概要:本文介绍了三维POD DMD程序在处理原网格数据方面的独特优势和技术细节。首先阐述了该程序能读取结构化和非结构化网格数据及其拓扑关系,在生成模态数据过程中保持原始网格形态而不需要进行网格插值操作。接着展示了简化版本的Python代码片段,揭示了读取网格数据和生成模态数据的核心逻辑。最后提到提供的辅助学习资料如代码、视频教程、Word教程和实例数据,帮助用户深入理解并掌握该程序的应用。 适合人群:从事计算流体力学领域的研究人员和技术爱好者,尤其是那些希望提高数据处理效率的人群。 使用场景及目标:适用于需要处理复杂网格数据的研究项目,旨在简化数据处理流程,提升工作效率,同时保持数据的原始特性。 其他说明:文中不仅提供了理论性的讲解,还有具体的代码示例和丰富的学习资源,使读者可以边学边练,快速上手。
融合双向路由注意力的多尺度X光违禁品检测.pdf
嵌入式八股文面试题库资料知识宝典-Linux_Shell基础使用.zip
嵌入式八股文面试题库资料知识宝典-联发科2021武汉嵌入式软件开发.zip
基于有限体积法Godunov格式的管道泄漏检测模型研究.pdf
嵌入式八股文面试题库资料知识宝典-ARM常见面试题目.zip
基于LWR问题的无证书全同态加密方案.pdf