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>

Oracle12c中功能及性能新特点之with子句的增强的更多相关文章

  1. Oracle12c中容错&amp;性能新特性之表空间组

    1.        简介 表空间组可以使用户消耗来自多个表空间的临时表空间.表空间组有如下特点: 1)      至少包含一个表空间.表空间组中包含的最大表空间数没有限制. 2)      和表空间共 ...

  2. Oracle12c中PL/SQL(DBMS_SQL)新特性之隐式语句结果(DBMS_SQL.RETURN_RESULT and DBMS_SQL.GET_NEXT_RESULT)

    隐式数据结果特性将能简化从其他数据库到Oracle12c存储过程迁移.1. 背景T-SQL中允许查询结果的隐式返回.例如:下面T-SQL存储过程隐式返回查询结果.CREATE PROCEDURE Ge ...

  3. Oracle12c中性能优化&amp;功能增强新特性之临时undo

    临时表最有意思的特点之一是undo段也存储在常规undo表空间中,而它们的undo反过来被redo保护,这会导致一些问题. 1)  写undo表空间需要数据库以读写模式打开,因此,只读数据库和物理备库 ...

  4. Oracle12c中SQL性能优化(SQL TUNING)新特性之自动重优化(automatic reoptimization)

    Oracle12c中的自动重优化 Oracle12c中的自适应查询优化有一系列不同特点组成.像自适应计划(AdaptivePlans)功能可以在运行时修改执行计划,但并不允许计划中连接顺序的改变.自动 ...

  5. Oracle12c中性能优化增强新特性之数据库智能闪存

    智能闪存功能最初在XD中引入.从Oracle11.2.0.2开始,除了用于XD存储,还可用于任何闪盘.Oracle12c中,不需卷管理器就可以使用闪盘. 1.  简介 智能闪存在solaris和lin ...

  6. Oracle12c中多宿主容器数据库(CDBs)和可插拔数据库(PDBs)新特性之运行脚本

    对开发者和DBA们来说,对shell脚本批量任务的影响成了多宿主选项带来的最大改变之一.因为多宿主环境通过服务来连接到可插拔数据库,因此,依靠CRON和OS认证成了换成多宿主环境后的一个最大问题.本文 ...

  7. Oracle12c中SQL优化(SQL TUNING)新特性之SQL计划指令

    SQL计划指令是Oracle12c中自适应查询优化的功能之一.SQL计划指令就像“额外的提醒” ,用以提醒优化器你先前选择了的计划并不是最优的,典型的是因为错误的势评估.错误的势评估往往是由统计信息缺 ...

  8. [译] OpenStack Ocata 版本中的 53 个新功能盘点

    原文链接:https://www.mirantis.com/blog/53-new-things-to-look-for-in-openstack-ocata/ 原文作者:Nick Chase, Ra ...

  9. oracle12c中新能优化新特性之热度图和自动数据优化

    1. Oracle12c热度图和自动数据优化 信息生命周期管理(ILM)是指在数据生命周期内管理它们的策略.依赖于数据的年龄和对应用的业务相关性,数据能被压缩,能被归档或移到低成本的存储上.简言之,I ...

随机推荐

  1. html5标签基础

    1.声明:<!DOCTYPE> HTML有多个不同的版本,只有完全明白页面中使用的确切HTML版本,浏览器才能完全正确的显示出HTML页面,这就是<!DOCTYPE>的用处. ...

  2. MT6592 经验积累

    1.build/target/product/xxxx.mk  新项目clone后,需要修改这里 如:build/target/product/x160v.mk PRODUCT_MODEL :=Phi ...

  3. obj-c编程15[Cocoa实例02]:KVC和KVO的实际运用

    我们在第16和第17篇中分别介绍了obj-c的KVC与KVO特性,当时举的例子比较fun,太抽象,貌似和实际不沾边哦.那么下面我们就用一个实际中的例子来看看KVC与KVO是如何运用的吧. 该例中用到了 ...

  4. 分布式Ruby解决之道

    其实用Druby很久了,今天需要完成一个进程数据同步的机制,我需要的不是运行速度快,不是用 linux / mac 下的扩展,而是独立,快速开发效率,方便最简单的Ruby环境可运行,可以吗? DRb( ...

  5. sql server对并发的处理-乐观锁和悲观锁

    https://www.cnblogs.com/dengshaojun/p/3955826.html sql server对并发的处理-乐观锁和悲观锁 假如两个线程同时修改数据库同一条记录,就会导致后 ...

  6. 【图文详解】HDFS基本原理

    本文主要详述了HDFS的组成结构,客户端上传下载的过程,以及HDFS的高可用和联邦HDFS等内容.若有不当之处还请留言指出. 当数据集大小超过一台独立的物理计算机的存储能力时,就有必要对它进行分区,并 ...

  7. STL读书笔记

    vector - 会自动增长的数组 vector又称为向量数组,他是为了解决程序中定义的数组是不能动态改变大小这个缺点而出现的.一般程序实现是在类创建的时候同时创建一个定长数组,随着数据不断被写入,一 ...

  8. 重构:以Java POI 导出EXCEL为例

    重构 开头先抛出几个问题吧,这几个问题也是<重构:改善既有代码的设计>这本书第2章的问题. 什么是重构? 为什么要重构? 什么时候要重构? 接下来就从这几个问题出发,通过这几个问题来系统的 ...

  9. Servlet总结二(文件路径)

    Servlet总结二(文件路径) 前言 前面我们说过ServletContext表示的是web容器中的上下文,下面我们也是用到ServletContext中的方法读取文件 读取WebRoot文件下的文 ...

  10. Search in rotated array two

    description: Follow up for "Search in Rotated Sorted Array":What if duplicates are allowed ...