您在这里:首页 > 学员专区 > 技术文章
Oracle视频
Oracle
CUUG课程

函数调用发生在SQL调用之前还是过程中

 

对于上一篇描述的DBMS_RANDOM.VALUE函数而言,显然函数的调用是发生在SQL语句的执行过程中。但是如果查看《SQL语句中常量的处理》这篇文章,可以看到对于TO_DATE之类的函数调用,当输入参数为常数时,Oracle会将其作为常数处理,在SQL语句执行之前就进行了调用。

同样都是函数,同样都以常数作为参数,同样都和表的列没有依赖,为什么有的函数在SQL调用前运行,而有的函数在SQL调用中执行。难道仅仅是因为一个是普通的函数,另一个是包中的函数。

问题显然与函数是否存储在包中没有关系,实际上是函数的一个特性控制了函数调用的时间。如果一个函数是确定性的,对于常量的输入,得到的结果也是常量,因此Oracle会在SQL运行之前对函数进行调用。而如果函数不是确定性的,Oracle无法保证函数输出的确定性,因此只能在SQL的运行时执行。

SQL> CREATE TABLE T (ID NUMBER);

Table created.

SQL> INSERT INTO T SELECT ROWNUM FROM DUAL CONNECT BY LEVEL <= 10000;

10000 rows created.

SQL> COMMIT;

Commit complete.

SQL> CREATE OR REPLACE FUNCTION F_TEST_DETER

  2  RETURN NUMBER DETERMINISTIC AS

  3  BEGIN

  4  DBMS_LOCK.SLEEP(0.01);

  5  RETURN 1;

  6  END;

  7  /

Function created.

SQL> CREATE OR REPLACE FUNCTION F_TEST_NODETER

  2  RETURN NUMBER AS

  3  BEGIN

  4  DBMS_LOCK.SLEEP(0.01);

  5  RETURN 1;

  6  END;

  7  /

Function created.

SQL> SELECT OBJECT_NAME, DETERMINISTIC

  2  FROM USER_PROCEDURES

  3  WHERE OBJECT_NAME LIKE 'F_TEST_%';

OBJECT_NAME                    DET

------------------------------ ---

F_TEST_NODETER                 NO

F_TEST_DETER                   YES

SQL> SET TIMING ON

SQL> SELECT * FROM T WHERE ID = F_TEST_DETER;

        ID

----------

         1

Elapsed: 00:00:00.02

SQL> SELECT * FROM T WHERE ID = F_TEST_NODETER;

        ID

----------

         1

Elapsed: 00:01:49.99

根据运行时间就可以判断处理,确定性函数只在SQL调用之前运行了一次,而非确定性函数则对于T表的每条记录都运行了一次。

如果将上一篇文章中的DBMS_RANDOM.VALUE包进行封装,并设置为确定性函数,则上一篇的查询结果就会改变:

SQL> SET TIMING OFF

SQL> CREATE OR REPLACE FUNCTION F_RANDOM

  2  RETURN NUMBER DETERMINISTIC AS

  3  BEGIN

  4  RETURN ROUND(DBMS_RANDOM.VALUE(1, 10000));

  5  END;

  6  /

Function created.

SQL> SELECT * FROM T_ID WHERE ID = F_RANDOM;

        ID

----------

      9548

SQL> SELECT * FROM T_ID WHERE ID = F_RANDOM;

        ID

----------

      6925

SQL> SELECT * FROM T_ID WHERE ID = F_RANDOM;

        ID

----------

      7783

SQL> SELECT * FROM T_ID WHERE ID = F_RANDOM;

        ID

----------

      7302

SQL> SELECT * FROM T_ID WHERE ID = ROUND(DBMS_RANDOM.VALUE(1, 10000));

        ID

----------

      2730

SQL> SELECT * FROM T_ID WHERE ID = ROUND(DBMS_RANDOM.VALUE(1, 10000));

        ID

----------

      9391

SQL> SELECT * FROM T_ID WHERE ID = ROUND(DBMS_RANDOM.VALUE(1, 10000));

no rows selected

SQL> SELECT * FROM T_ID WHERE ID = ROUND(DBMS_RANDOM.VALUE(1, 10000));

        ID

----------

      3935

SQL> SELECT * FROM T_ID WHERE ID = ROUND(DBMS_RANDOM.VALUE(1, 10000));

        ID

----------

      6132

      7810

这很好的说明了确定性和非确定性函数的区别。

最后通过例子说明问题之和函数的确定性有关,和函数是否在包中无关:

SQL> CREATE OR REPLACE PACKAGE PA_TEST AS

  2  FUNCTION F_DETER RETURN NUMBER DETERMINISTIC;

  3  FUNCTION F_NODETER RETURN NUMBER;

  4  END;

  5  /

Package created.

SQL> CREATE OR REPLACE PACKAGE BODY PA_TEST AS

  2  FUNCTION F_DETER RETURN NUMBER DETERMINISTIC AS

  3  BEGIN

  4  DBMS_LOCK.SLEEP(0.01);

  5  RETURN 1;

  6  END;

  7  FUNCTION F_NODETER RETURN NUMBER AS

  8  BEGIN

  9  DBMS_LOCK.SLEEP(0.01);

 10  RETURN 1;

 11  END;

 12  END;

 13  /

Package body created.

SQL> SET TIMING ON

SQL> SELECT * FROM T_ID WHERE ID = PA_TEST.F_DETER;

        ID

----------

         1

Elapsed: 00:00:00.02

SQL> SELECT * FROM T_ID WHERE ID = PA_TEST.F_NODETER;

        ID

----------

         1

Elapsed: 00:01:49.98

相关文章 [上一篇] 通过数据库链执行DML所需权限
010-88589926(88587026)
CUUG热门培训课程
Oracle DBA就业培训
CUUG名师
网络课程
技术沙龙
最新动态

总机:(010)-88589926,88589826,88587026 QQ讨论群:243729577 182441349 邮箱:cuug_bj@cuug.com
通信地址:北京市海淀区紫竹院路98号北京化工大学科技园609室(CUUG)邮政编码:100089 
中国UNIX用户协会 Copyright 2010  ALL Rights Reserved 北京神脑资讯技术有限公司
京ICP备11008061号  京公网安备110108006275号