zoukankan      html  css  js  c++  java
  • Oracle的pipelined函数实现高性能大数据处理

    从Oracle 8开始,我们就可以从一个collection类型的数据集合中查询出数据,这个集合称之为“虚拟表“。它的方法是“SELECT FROM TABLE(CAST(plsql_function AS collection_type))”,据说该方法在处理大数据量时会有内存方面的限制。到了Oracle 9i之后,一个称为pipelined表函数的技术被推出来。他和普通的表函数很类似,但还有有一些显著的差别。
    第一,pipelined函数处理的数据,是以管道的方式,或者说是流的方式从预先准备的小数组中展现给用户,而普通表函数将数据全部准备好再展现给用户。
    第二,pipelined函数可以并发,这意味着PLSQL可以同一时间在多个进程上执行。
    第三,pipelined函数可以很容易将存储过程转换成用bulk操作的行,有利于实现的复杂转换逻辑的SQL。

     

    (miki西游 @mikixiyou 原文链接: http://mikixiyou.iteye.com/blog/1628397 )

     

    了解pipelined函数的最佳方法是看一个简单的例子。对于任何一个pipelined函数,都必须有两点要求。
    1、一个定义在模式中或者包中collection类型;
    2、一个单独的PL/SQL函数或一个包中的函数,函数的返回类型后面必须加pipelined关键字;


    在下面的例子中,我们将创建一个简单的pipelined函数,输出若干行记录。首先需要一个collection类型,用于格式化输出。

    Sql代码  收藏代码
    1. CREATE OR REPLACE TYPE number_ntt AS TABLE OF NUMBER;  
     


    Oracle会使用这个类型缓存少量的记录作为pipelined函数调用时的输出。我们创建一个简单的pipelined函数。

    Sql代码  收藏代码
    1. CREATE OR REPLACE FUNCTION row_generator(rows_in IN PLS_INTEGER)  
    2.   RETURN number_ntt  PIPELINED  
    3. IS  
    4. BEGIN  
    5.   FOR i IN 1 .. rows_in LOOP  
    6.     PIPE ROW(i);  
    7.   END LOOP;  
    8.   RETURN;  
    9. END;  
     

    在这个SQL中:
    在函数定义部分的关键字pipelined是pipelined函数定义的关键,返回的类型必须是事先定义的collection类型,如这里是number_tt。
    在函数主体部分的”PIPE ROW”是将一个单行记录写入到collection流中。记录中所有字段的类型必须和collection类型中所有字段匹配。
    在函数主体部分的“return“的值是一个空值,而不是有任何符合collection类的值。
    这些就是pipelined函数定义时需要严格遵守的规则。

     


    现在已经创建好一个pipelined函数,我们可以测试一下。

    Sql代码  收藏代码
    1. SQL> select * from TABLE( row_generator(10) );  
    2.    
    3. COLUMN_VALUE  
    4. ------------  
    5.            1  
    6.            2  
    7.            3  
    8.            4  
    9.            5  
    10.            6  
    11.            7  
    12.            8  
    13.            9  
    14.           10  
    15.    
    16. 10 rows selected  
     


    将pipelined函数row_generator放到一个“TABLE”操作符中,虚拟成一个数据源,类似表或视图。这里虚拟表只有一个字段,名称“COLUMN_VALUE“是其默认值。更复杂的输出则需要将collection定义得更复杂些,使用到object或者record。


    我们通过一个例子比较一下pipelined函数或普通的表函数在返回collection时有何差异。


    第一步,创建普通的表函数,返回colletion类型。

    Sql代码  收藏代码
    1. CREATE OR REPLACE FUNCTION table_function RETURN number_ntt AS  
    2.   nt number_ntt := number_ntt();  
    3. BEGIN  
    4.   FOR i IN 1 .. 500000 LOOP  
    5.     if (mod(i, 10000) = 0) then  
    6.       nt.EXTEND;  
    7.       nt(nt.LAST) := i;  
    8.     end if;  
    9.    
    10.   END LOOP;  
    11.   RETURN nt; --<-- return whole collection  
    12. END table_function;  

     


    第二步,创建pipelined函数,返回的也是collection类型

    Sql代码  收藏代码
    1. CREATE OR REPLACE FUNCTION pipelined_function RETURN number_ntt  
    2.   PIPELINED AS  
    3. BEGIN  
    4.   FOR i IN 1 .. 500000 LOOP  
    5.     if (mod(i, 10000) = 0) then  
    6.       PIPE ROW(i); --<-- send row to consumer  
    7.     end if;  
    8.   END LOOP;  
    9.   RETURN;  
    10. END pipelined_function;  
     



    函数的功能都是将能和1000整除的数字输出出来。
    再创建一个输出时间到毫秒的函数,用于测试两个函数的输出特点。

    Sql代码  收藏代码
    1. CREATE FUNCTION get_time RETURN TIMESTAMP IS  
    2. BEGIN  
    3.    RETURN LOCALTIMESTAMP;  
    4. END get_time;  
    5. /  
     


    第三步,测试两个函数


    测试普通函数如下:

     

    Sql代码  收藏代码
    1. ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'HH24:MI:SS.FF3';  
    2. SELECT get_time() AS ts FROM DUAL;  
    3. SELECT column_value, get_time() AS ts FROM TABLE(table_function);  
    4. SQL>  
    5.    
    6. TS  
    7. --------------------------------------------------------------------------------  
    8. 15:27:26.031  
    9.    
    10. COLUMN_VALUE TS  
    11. ------------ --------------------------------------------------------------------------------  
    12.       100000 15:27:26.218  
    13.       200000 15:27:26.218  
    14.       300000 15:27:26.218  
    15.       400000 15:27:26.218  
    16.       500000 15:27:26.218  
    17.    
    18. SQL>  

     

    结果显示,所有记录都是同一时间输出。

     

     

    测试pipelined函数如下:

     

    Sql代码  收藏代码
    1. SELECT get_time() AS ts FROM DUAL;  
    2. SELECT column_value, get_time() AS ts FROM TABLE(pipelined_function);  
    3. TS  
    4. --------------------------------------------------------------------------------  
    5. 15:27:26.265  
    6.    
    7. COLUMN_VALUE TS  
    8. ------------ --------------------------------------------------------------------------------  
    9.       100000 15:27:26.312  
    10.       200000 15:27:26.343  
    11.       300000 15:27:26.390  
    12.       400000 15:27:26.421  
    13.       500000 15:27:26.453  
     


     
    结果显示,所有记录都是逐次输出。
    这点对于用户的UI太重要了。试想,如果执行一个查询,过了10秒钟才显示出所有的结果好,还是还是每秒都显示一些记录,知道10秒钟显示完毕好?

    如果这个输出的结果集再放到到百万记录,两个函数对PGA内存的消耗又完全不一样,这点更重要

  • 相关阅读:
    HDFS基本原理及数据存取实战
    关于软件工程的思考06:微软解决方案框架MSF
    关于软件工程的思考05:敏捷流程
    关于软件工程的思考04:团队和流程
    关于软件工程的思考03:两人合作
    关于软件工程的思考02:软件工程师的成长
    关于软件工程的思考01:个人技术流程
    Linux31:磁盘配额与磁盘阵列简介
    Linux30:LVM和SELinux简介
    Linux29:SSH
  • 原文地址:https://www.cnblogs.com/pekkle/p/6568803.html
Copyright © 2011-2022 走看看