1.1 逐行处理

在一个典型的逐行处理程序中,代码首先打开一个游标,然后遍历从游标返回的行,并且处理每一行数据。这种基于循环的处理方式是非常不赞成使用的,它会导致不可扩展的代码。代码清单1-1显示了一个使用这种结构的例子。

代码清单1-1 逐行处理


在代码清单1-1中,程序声明了一个游标c1,然后用游标for循环隐式地打开了这个游标,对从游标c1取出的每一行,程序查询customers表,并把first_name和last_name的值填充到变量,随后插入一行数据到top_sales_customers表。

代码清单1-1的编程方法很有问题。即使在循环中调用的SQL语句是高度优化的,程序的执行还是会消耗大量时间。假设查询customers表的SQL语句消耗0.1秒,INSERT语句也消耗0.1秒,那么在循环中每次执行就要0.2秒。如果游标c1取出了100 000行,那么总时间就是100 000乘以0.2秒,即20 000秒,也就是大约5.5小时。很难去优化这个程序的结构。基于显而易见的理由,Tom Kyte把这种处理方式定义为慢之又慢的处理(slow-by-slow processing)。

注意 本章的例子采用SH模式(schema),这是Oracle公司提供的范例模式之一。要安装这个范例模式,Oracle提供了安装软件。可以从http://download.oracle.com/otn/solaris/oracle11g/ R2/solaris.sparc64_11gR2_examples.zip下载Solaris平台11gR2版本的样例软件。软件解压缩后的目录中含有安装说明。Oracle网站也提供了其他平台和版本的范例软件的Zip压缩包。

代码清单1-1的代码还有一个固有的问题。从PL/SQL的循环中调用的SQL语句会反复在PL/SQL引擎和SQL引擎之间切换执行,这种两个环境之间的切换称作上下文切换。上下文切换增加了程序运行的时间,并增加不必要的CPU开销。你应当通过消除或减少这种两个环境之间的切换来减少上下文切换的次数。

一般应当禁止逐行处理,更好的编程实践是把代码清单1-1的程序转换成一个SQL语句。代码清单1-2重写了代码,完全避免了PL/SQL。

代码清单1-2 重写逐行处理的代码

代码清单1-2除了解决逐行处理的缺陷以外,还有更多的优势。重写后的SQL语句可以使用并行执行来调优,使用多个并行执行进程可以大幅地减少执行时间。并且,代码变得简明且可读性强。

注意 如果将PL/SQL的循环代码重写为连接(join),需要考虑重复问题。如果在customers表中cust_id列有重复值,那么重写后的SQL语句取回的行比你想要的行多。不过,在这个特定的例子中,customers表的cust_id列上有主键,因此在cust_id列上使用等值连接不存在重复的危险。

目录