zoukankan      html  css  js  c++  java
  • Oracle12c中功能及性能新特点之with子句的增强

    1. 设置
    创建测试表。
    DROP TABLE test PURGE;


    CREATE TABLE test AS
    SELECT 1 AS id
    FROM   dual
    CONNECT BY level <= 1000000;


    2. WITH子句中的函数
    WITH子句声明部分可用来定义函数,如下所示。
    WITH
      FUNCTION with_function(p_id IN NUMBER) RETURN NUMBER IS
      BEGIN
        RETURN p_id;
      END;
    SELECT with_function(id)
    FROM   test
    WHERE  rownum = 1
    /


    WITH_FUNCTION(ID)
    -----------------
                    1


    SQL>
    有意思的是,当WITH子句中包含PL/SQL声明时,分号";"不再能用作SQL语句的终止符。如果我们使用它,SQL*Plus会等待更多命令文本输入。即使在官方文档中,也是使用了分号“;”和反斜杠“/”的组合。 
    从名字解析角度看,WITH子句PL/SQL声明部分定义的函数比当前模式中其他同名对象优先级要高。
    3. WITH子句中的过程
    即使不被使用,我们也可以在声明部分定义过程。
    SET SERVEROUTPUT ON


    WITH
      PROCEDURE with_procedure(p_id IN NUMBER) IS
      BEGIN
        DBMS_OUTPUT.put_line('p_id=' || p_id);
      END;
    SELECT id
    FROM   test
    WHERE  rownum = 1
    /


            ID
    ----------
             1


    SQL>
    现实中,如果你打算从声明部分的函数中调用一个过程,你可以在声明部分定义一个过程。
    WITH
      PROCEDURE with_procedure(p_id IN NUMBER) IS
      BEGIN
        DBMS_OUTPUT.put_line('p_id=' || p_id);
      END;


      FUNCTION with_function(p_id IN NUMBER) RETURN NUMBER IS
      BEGIN
        with_procedure(p_id);
        RETURN p_id;
      END;
    SELECT with_function(id)
    FROM   test
    WHERE  rownum = 1
    /


    WITH_FUNCTION(ID)
    -----------------
                    1


    p_id=1
    SQL>
    4. PL/SQL支持
    PL/SQL并不支持该特点。如果视图在PL/SQL中使用将会报编译错误,如下所示。
    BEGIN
      FOR cur_rec IN (WITH
                        FUNCTION with_function(p_id IN NUMBER) RETURN NUMBER IS
                        BEGIN
                          RETURN p_id;
                        END;
                      SELECT with_function(id)
                      FROM   test
                      WHERE  rownum = 1)
      LOOP
        NULL;
      END LOOP;
    END;
    /
    FUNCTION with_function(p_id IN NUMBER) RETURN NUMBER IS
                                 *
    ERROR at line 3:
    ORA-06550: line 3, column 30:
    PL/SQL: ORA-00905: missing keyword
    ORA-06550: line 2, column 19:
    PL/SQL: SQL Statement ignored
    ORA-06550: line 5, column 34:
    PLS-00103: Encountered the symbol ";" when expecting one of the following:
    loop




    SQL>
    使用动态SQL可以绕过这个限制。
    SET SERVEROUTPUT ON
    DECLARE
      l_sql     VARCHAR2(32767);
      l_cursor  SYS_REFCURSOR;
      l_value   NUMBER;
    BEGIN
      l_sql := 'WITH
                  FUNCTION with_function(p_id IN NUMBER) RETURN NUMBER IS
                  BEGIN
                    RETURN p_id;
                  END;
                SELECT with_function(id)
                FROM   test
                WHERE  rownum = 1';
      
      OPEN l_cursor FOR l_sql;
      FETCH l_cursor INTO l_value;
      DBMS_OUTPUT.put_line('l_value=' || l_value);
      CLOSE l_cursor;
    END;
    /
    l_value=1


    PL/SQL procedure successfully completed.


    SQL>
    PL/SQL中将该特点用于静态SQL是未来版本的事情。
    5. 性能优势
    定义行内PL/SQL代码的原因是为了改善性能。下面创建常规函数来进行比较。
    CREATE OR REPLACE FUNCTION normal_function(p_id IN NUMBER) RETURN NUMBER IS
    BEGIN
      RETURN p_id;
    END;
    /
    运行如下测试,测量行内函数查询消耗的时间和CPU。
    SET SERVEROUTPUT ON
    DECLARE
      l_time    PLS_INTEGER;
      l_cpu     PLS_INTEGER;
      
      l_sql     VARCHAR2(32767);
      l_cursor  SYS_REFCURSOR;
      
      TYPE t_tab IS TABLE OF NUMBER;
      l_tab t_tab;
    BEGIN
      l_time := DBMS_UTILITY.get_time;
      l_cpu  := DBMS_UTILITY.get_cpu_time;


      l_sql := 'WITH
                  FUNCTION with_function(p_id IN NUMBER) RETURN NUMBER IS
                  BEGIN
                    RETURN p_id;
                  END;
                SELECT with_function(id)
                FROM   test';
                
      OPEN l_cursor FOR l_sql;
      FETCH l_cursor
      BULK COLLECT INTO l_tab;
      CLOSE l_cursor;
      
      DBMS_OUTPUT.put_line('WITH_FUNCTION  : ' ||
                           'Time=' || TO_CHAR(DBMS_UTILITY.get_time - l_time) || ' hsecs ' ||
                           'CPU Time=' || (DBMS_UTILITY.get_cpu_time - l_cpu) || ' hsecs ');


      l_time := DBMS_UTILITY.get_time;
      l_cpu  := DBMS_UTILITY.get_cpu_time;


      l_sql := 'SELECT normal_function(id)
                FROM   test';
                
      OPEN l_cursor FOR l_sql;
      FETCH l_cursor
      BULK COLLECT INTO l_tab;
      CLOSE l_cursor;
      
      DBMS_OUTPUT.put_line('NORMAL_FUNCTION: ' ||
                           'Time=' || TO_CHAR(DBMS_UTILITY.get_time - l_time) || ' hsecs ' ||
                           'CPU Time=' || (DBMS_UTILITY.get_cpu_time - l_cpu) || ' hsecs ');
     
    END;
    /
    WITH_FUNCTION  : Time=45 hsecs CPU Time=39 hsecs
    NORMAL_FUNCTION: Time=129 hsecs CPU Time=113 hsecs


    PL/SQL procedure successfully completed.


    SQL>
    从该测试可以看到,行内函数值消耗了普通函数三分之一的时间和CPU。
    6. PRAGMA UDF
    12c 版本前,人们经常会提到PRAGMA UDF,据说可通过行内PL/SQL来提升性能,同时,允许在SQL语句外定义PL/SQL对象。下列代码用PRAGMA重新定义之前的常规函数。
    CREATE OR REPLACE FUNCTION normal_function(p_id IN NUMBER) RETURN NUMBER IS
      PRAGMA UDF;
    BEGIN
      RETURN p_id;
    END;
    /
    一旦函数被编译,从先前部分运行该函数会产生相当有趣的结果。
    SET SERVEROUTPUT ON
    DECLARE
      l_time    PLS_INTEGER;
      l_cpu     PLS_INTEGER;
      
      l_sql     VARCHAR2(32767);
      l_cursor  SYS_REFCURSOR;
      
      TYPE t_tab IS TABLE OF NUMBER;
      l_tab t_tab;
    BEGIN
      l_time := DBMS_UTILITY.get_time;
      l_cpu  := DBMS_UTILITY.get_cpu_time;


      l_sql := 'WITH
                  FUNCTION with_function(p_id IN NUMBER) RETURN NUMBER IS
                  BEGIN
                    RETURN p_id;
                  END;
                SELECT with_function(id)
                FROM   test';
                
      OPEN l_cursor FOR l_sql;
      FETCH l_cursor
      BULK COLLECT INTO l_tab;
      CLOSE l_cursor;
      
      DBMS_OUTPUT.put_line('WITH_FUNCTION  : ' ||
                           'Time=' || TO_CHAR(DBMS_UTILITY.get_time - l_time) || ' hsecs ' ||
                           'CPU Time=' || (DBMS_UTILITY.get_cpu_time - l_cpu) || ' hsecs ');


      l_time := DBMS_UTILITY.get_time;
      l_cpu  := DBMS_UTILITY.get_cpu_time;


      l_sql := 'SELECT normal_function(id)
                FROM   test';
                
      OPEN l_cursor FOR l_sql;
      FETCH l_cursor
      BULK COLLECT INTO l_tab;
      CLOSE l_cursor;
      
      DBMS_OUTPUT.put_line('NORMAL_FUNCTION: ' ||
                           'Time=' || TO_CHAR(DBMS_UTILITY.get_time - l_time) || ' hsecs ' ||
                           'CPU Time=' || (DBMS_UTILITY.get_cpu_time - l_cpu) || ' hsecs ');
     
    END;
    /
    WITH_FUNCTION  : Time=44 hsecs CPU Time=40 hsecs
    NORMAL_FUNCTION: Time=33 hsecs CPU Time=29 hsecs


    PL/SQL procedure successfully completed.


    SQL>
    用PRAGMA UDF的独立函数似乎一直比行内函数还快。
    我以为从PL/SQL中调用PRAGMA UDF定义的函数会失败,可事实似乎不是这么个情况。
    DECLARE
      l_number NUMBER;
    BEGIN
      l_number := normal_function(1);
    END;
    /


    PL/SQL procedure successfully completed.


    SQL>
    7. WITH_PLSQL Hint
    如果包含PL/SQL声明部分的查询不是顶级查询,那么,顶级查询必须包含WITH_PLSQL hint。没有该hint,语句在编译时会失败,如下所示。
    UPDATE test a
    SET a.id = (WITH
                  FUNCTION with_function(p_id IN NUMBER) RETURN NUMBER IS
                  BEGIN
                    RETURN p_id;
                  END;
                SELECT with_function(a.id)
                FROM   dual);
    /
    SET a.id = (WITH
                *
    ERROR at line 2:
    ORA-32034: unsupported use of WITH clause




    SQL>
    加上WITH_PLSQL hint后,语句编译通过且如期运行。
    UPDATE /*+ WITH_PLSQL */ t1 a
    SET a.id = (WITH
                  FUNCTION with_function(p_id IN NUMBER) RETURN NUMBER IS
                  BEGIN
                    RETURN p_id;
                  END;
                SELECT with_function(a.id)
                FROM   dual);
    /


    1000000 rows updated.


    SQL>
    8. DETERMINISTIC Hint
    就像刘易斯指出的那样,WITH子句中使用函数会阻止发生DETERMINISTIC优化。
    SET TIMING ON ARRAYSIZE 15


    WITH
      FUNCTION slow_function(p_id IN NUMBER) RETURN NUMBER DETERMINISTIC IS
      BEGIN
        DBMS_LOCK.sleep(1);
        RETURN p_id;
      END;
    SELECT slow_function(id)
    FROM   test
    WHERE  ROWNUM <= 10;
    /


    SLOW_FUNCTION(ID)
    -----------------
    1
    1
    1
    1
    1
    1
    1
    1
    1
    1


    10 rows selected.


    Elapsed: 00:00:10.07
    SQL>
    9. 标量子查询缓冲
    前面部分,我们看到行内函数定义对DETERMINISTIC hint优化上的负面影响。 庆幸的是,标量子查询缓冲并不被同样被影响。
    SET TIMING ON


    WITH
      FUNCTION slow_function(p_id IN NUMBER) RETURN NUMBER DETERMINISTIC IS
      BEGIN
        DBMS_LOCK.sleep(1);
        RETURN p_id;
      END;
    SELECT (SELECT slow_function(id) FROM dual)
    FROM   test
    WHERE  ROWNUM <= 10;
    /


    (SELECTSLOW_FUNCTION(ID)FROMDUAL)
    ---------------------------------
    1
    1
    1
    1
    1
    1
    1
    1
    1
    1


    10 rows selected.


    Elapsed: 00:00:01.04
    SQL>

  • 相关阅读:
    Java实现 蓝桥杯 算法训练 画图(暴力)
    Java实现 蓝桥杯 算法训练 画图(暴力)
    Java实现 蓝桥杯 算法训练 相邻数对(暴力)
    Java实现 蓝桥杯 算法训练 相邻数对(暴力)
    Java实现 蓝桥杯 算法训练 相邻数对(暴力)
    Java实现 蓝桥杯 算法训练 Cowboys
    Java实现 蓝桥杯 算法训练 Cowboys
    55. Jump Game
    54. Spiral Matrix
    50. Pow(x, n)
  • 原文地址:https://www.cnblogs.com/lhdz_bj/p/9063672.html
Copyright © 2011-2022 走看看