分享

【新提醒】[转载]本地动态SQL中的高级论题

 姑苏慕容凡 2014-08-22

http://www./forum.php?mod=viewthread&tid=5444

建立动态PL/SQL块
在PL/SQL中,动态地构造和执行代码块可能是最另人兴奋的事。想一下:当一个用户正在运行你的应用程序,这个软件可以做下面的任何事情:

建立一个程序,它有一个含有全局性可访问数据结构的包。
通过名字获得(和改变)全局变量值。
在编译时调用未命名的函数和过程。
当然,这种较为笼统的工作确实有一些额外的复杂因素。下面是你操作PL /SQL块和NDS时要记住的技巧:


动态字符串必须是一个有效的PL/SQL块。它必须以关键字DECLARE或BE GIN开始,以一个END语句和分号结束。
在你的动态块里,仅能获得具有全局范围的PL/SQL代码元素(孤立的函数、过程以及在包的说明中定义的元素)。动态PL/SQL块在本地封装块的范围外执行。在一个动态PL/SQL字符串内产生的错误可被执行字符串的局部块捕捉并处理。
Errors raised within a dynamic PL/SQL string can be trapped and handled by the local block in which the string was executed.
让我们用两个(一个简单,一个比较复杂)基于真实生活经历的例子来探索这些规则。

一个基本的灵活工具。 首先我将建立一个短小的工具来执行动态 PL/SQL:


/* File: dynplsql.sp */
CREATE OR REPLACE PROCEDURE dynPLSQL (blk IN VARCHAR2)
IS
BEGIN
   EXECUTE IMMEDIATE
      'BEGIN ' || RTRIM (blk, ';') || ';  END;';
END;
/
这一程序集中了许多前面提到的PL/SQL执行规则。用BEGIN-END对将字符串封装起来,我保证无论我加入什么,都会被作为一个有效的PL/SQL块运行。例如,我可以如此简单地动态执行calc-totals过程:



SQL> exec dynPLSQL ('calc_totals');
现在让我们超越这一普遍性的例子,来看一些更具体的例子。

一个真实的犯罪故事(写出这种代码的方法是一种真实的犯罪。)我曾经在一个保险公司呆过一段时间,在那里,建立保险单的原则与IRS建立税法的原则是一样的:越复杂越好。例如,一张保险单可能具有成百行的细则项目,每一项有一数值与之相联系,每一项适用于或不适用于某一特定索赔要求。

在公司里,开发人员采取了如下方法:对保险单中的每一行号,他们都写了一行处理过程。于是,为了处理第一行,他们将调用:


process_line1
并且,如果第514行适用于索赔要求,他们将调用:


process_line514
因此,他们最终的程序是这样:


CREATE OR REPLACE PROCEDURE process_line  (line IN INTEGER)
IS
BEGIN
   IF line = 1 THEN process_line1;
   ELSIF line = 2 THEN process_line2;
   ...
   ELSIF line = 514 THEN process_line514;
   ...
   END IF;
END;
多年以来,这一程序像牡蛎里的珍珠在成长:process-lineN 程序在一层又一层地扩充。令人悲哀地是,珍珠的层次产生了美丽,而程序中的层次却使得编译和执行慢了许多。

当然,动态SQL极好地适应于这一情节,我在代码中定义了一个清楚的模式:对第N行运行process-lineN ,并将其翻译成动态构建PL/SQL块,如下所示:


CREATE OR REPLACE PROCEDURE process_line (line IN INTEGER)
IS
BEGIN
   EXECUTE IMMEDIATE
      'BEGIN process_line' || line || ';  END;';
END;
于是这一由成千上万行代码组成的程序缩减成了一个可执行的语句。


本地动态SQL的乐趣
虽然我能够不断地提供动态PL/SQL的令人兴奋的应用实例(例如能够执行你交给它的任意表达式或函数的计算器程序,或者一个列表解析引擎,它将列表元素返回到你指定的集合中),但是,我希望此时你已经掌握了其中的要点。记住我们在这曾经讨论过的问题,尤其是那些关于尽可能地捆绑变量、使用AUTHID CURRENT-USER 子句来正确解析授权,以及不停地捕获错误等方面的问题,你会充分准备好创建自己精巧的、动态结构的SQL和PL/SQL字符串。
Steven Feuerstein, 是Oracle PL/SQL语言的重要专家,是关于PL/SQL方面五本书的作者:《Oracle PL/SQL编程》,《利用包的高级Oracle PL/SQL编程》,《Oracle内置包》,《Oracle8i特性的Oracle PL/SQL编程指南》,《Oracle PL/SQL开发人员工作手册》以及《PL/SQL CD 书架》(全部由O’Reilly &Associates出版)。Steven还为RevealNet公司 (www.) 构建了PL/SQL开发员实用工具程序,并通过PL/SolutionS(www.)提供培训和咨询。

在动态SQL中使用BULK COLLECT
Oracle8i提供了许多不同的改进PL/SQL程序性能的方法。一个非常有用的新特性就是BULK COLLECT 子句,使用它可以用一句简单的SELECT INTO语句查询大量的行。这些行直接被放置在各种集合(嵌套表,索引表,或不同的数组)中。
下面是一个使用BULK COLLECT检索关于我编写的或合著的PL/SQL书籍的多行信息的例子:


CREATE TYPE titles_t IS TABLE OF book.title%TYPE;
/
CREATE TYPE authors_t IS TABLE OF book.author%TYPE;
/
CREATE TYPE page_count_t IS TABLE OF book.page_count%TYPE;
/

DECLARE
   titles titles_t := titles_t ();
   authors authors_t := authors_t ();
   page_counts page_count_t := page_count_t ();
BEGIN
   SELECT author, title, page_count
     BULK COLLECT INTO authors, titles, page_counts
     FROM book
    WHERE author LIKE 'FEUERSTEIN%';
END;
你会注意到,只用一个查询,我们就方便地检索了所有的行。

现在 BULK COLLECT是一个PL/SQL语句,而不是SQL语言的一部分。因此,如果想用动态SQL执行一个BULK COLLECT操作,我必须在一个PL/SQL块内进行操作。例如,假设我想创建一个通用函数,能够检索任意表中任意列的所有VARCHAR2型值,并且尽可能有效地这样做。

BULK COLLECT和NDS共同提供了达到这一目的的工具。我粗略地拼合了一个PL/SQL块,它包含一个非常通用的BULK COLLECT查询。我为集合包括了一个占位符,然后用包含实际集合的U SING子句执行这个块,这一集合容纳了所有的行。

如下列所示,all_strings.fun文件不仅包含函数的定义,而且包含了显示由函数填充的集合内容的程序,以及基于雇员表的示范脚本:


/* file: all_strings.fun */
CREATE OR REPLACE FUNCTION all_strings (
   tab_in IN VARCHAR2,
   col_in IN VARCHAR2,
   where_in IN VARCHAR2 := NULL
)
   RETURN strings_t
IS
   dynstr VARCHAR2 (2000);
   l_strings strings_t := strings_t ();
BEGIN
   dynstr :=
      'BEGIN
          SELECT ' || col_in ||
          ' BULK COLLECT INTO :strings ' ||
          ' FROM ' || tab_in ||
         ' WHERE ' || NVL (where_in, '1 = 1') ||
         ';
       END;';
   EXECUTE IMMEDIATE dynstr USING  OUT l_strings;
   RETURN l_strings;
END;
/

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多