ITPUB??ì3
12月微软Hyper-V虚拟化沙龙主题征集
ITPUB论坛 » Oracle开发 » 很少受关注的SQL中一些函数调用秘密

标题: [精华] 很少受关注的SQL中一些函数调用秘密
  本主题由 System 于 2008-9-11 05:00 解除限时置顶 
离线 nyfor
Oracle Fan


来自 苏州
精华贴数 7
个人空间 0
技术积分 8319 (155)
社区积分 475 (1610)
注册日期 2002-1-6
论坛徽章:17
现任管理团队成员     
      

发表于 2008-9-5 17:49 
很少受关注的SQL中一些函数调用秘密

提示: 本帖阅读需要很有耐性, 可能需要仔细想SQL的结果

SQL中如果你调用了函数, 则你在某些特殊情况下, 需要注意函数的调用次数.
通常是在函数对于同一个输入参数, 但是却会返回不同的返回值的情况下需要注意.
之前的一个小小题目: http://www.itpub.net/thread-1051229-1-1.html
请通过该主题了解本帖即将讨论的内容.此贴内容:

QUOTE:
已知表 t ,执行 select count(*) from t; 返回值 3;

那么以下这个SQL语句的返回值可能的结果有几个(不是指返回值是多少,而是问可能有几种值)?
也就是执行以下SQL无限多次, 其返回的结果有几个不同的值?

select count(*) from t
where dbms_random.value < dbms_random.value;

A. 1 种  B. 2 种  C. 3 种  D. 4 种

答案选择 B.2种, 结果值只有0 和 3.

我们首先创建一个Package, 可以通过它了解函数的调用情况.
create or replace package pkg
is
  function get_n return number;
  function inc_n return number;
  procedure reset_n;
end;
/

create or replace package body pkg
is
  g_n number;
  function get_n return number
  is
  begin
    return g_n;
  end;
  
  function inc_n return number
  is
  begin
    g_n := g_n + 1;
    return g_n;
  end;
  
  procedure reset_n
  is
  begin
    g_n := 0;
  end;
begin
  g_n := 0;
end;
/

我们再创建一个测试表:
create table t(id number);
insert into t values(1);
insert into t values(2);
insert into t values(3);
commit;

至此, 我们开始SQL中的函数调用调研.
1.
SQL> exec pkg.reset_n;

PL/SQL procedure successfully completed

SQL> select pkg.inc_n,pkg.inc_n from t;

     INC_N      INC_N
---------- ----------
         1          2
         3          4
         5          6
SQL> select pkg.inc_n,pkg.inc_n from t where id = 2;

     INC_N      INC_N
---------- ----------
         1          2

我们可以看到出现在select列上的函数调用对于返回的每一行记录都会调用一次, 而如果多次出现在select列表中, 将会执行多次.

[ 本帖最后由 nyfor 于 2008-9-8 16:34 编辑 ]


__________________
 
帮助别人,也在帮助自己!
Oracle Version 9.2.0
Oracle Version 10.2.0

看别人不顺眼是自己修养不够!
只看该作者    顶部
离线 junsansi
无名扫把


来自 bj
精华贴数 8
个人空间 7925
技术积分 8974 (137)
社区积分 1137 (926)
注册日期 2006-7-17
论坛徽章:93
现任管理团队成员2008北京奥运纪念徽章:举重2008北京奥运纪念徽章:现代五项2008北京奥运纪念徽章:皮划艇静水2008北京奥运纪念徽章:水球2008北京奥运纪念徽章:拳击
生肖徽章2007版:鼠生肖徽章2007版:鼠生肖徽章2007版:鼠生肖徽章2007版:鼠生肖徽章2007版:鼠生肖徽章2007版:鼠

发表于 2008-9-5 23:01 
支持~~


__________________
我为PUB做贡献: Itpub徽章统计与分析的小工具 出手救市,长期收狗收老鼠:):)~~
============================================
http://www.5ienet.com我爱我佳
扫地不过是我表面上的工作,我真正的身份其实是无名老僧------------------手里那根扫把~~~~
只看该作者    顶部
离线 nyfor
Oracle Fan


来自 苏州
精华贴数 7
个人空间 0
技术积分 8319 (155)
社区积分 475 (1610)
注册日期 2002-1-6
论坛徽章:17
现任管理团队成员     
      

发表于 2008-9-8 15:45 
junsansi 居然把我的沙发给抢了.

2.
SQL> exec pkg.reset_n;

PL/SQL procedure successfully completed

SQL> select pkg.inc_n from t where pkg.inc_n = 2;

     INC_N
----------

SQL> /

     INC_N
----------
         3
         4
         5

SQL> exec pkg.reset_n;

PL/SQL procedure successfully completed

SQL> select pkg.inc_n from t where pkg.inc_n = id;

     INC_N
----------
         2
SQL> select pkg.get_n from dual;

     GET_N
----------
         4
SQL> exec pkg.reset_n;

PL/SQL procedure successfully completed

SQL> select pkg.inc_n from t where pkg.inc_n = id + 1;

     INC_N
----------

SQL> select pkg.get_n from dual;

     GET_N
----------
         3

这里要好好理解得出的结果.
我们发现出现在 where 条件中的函数, 如果与该函数有关的条件表达式(不是整个的Where条件表达式), 如果没有涉及到表中的资料信息, 那么Oracle将认为是一个常量条件表达式, 只会计算一次,也就是函数在Where条件筛选部分只会调用一次.
而如果与表中数据相关, 则对每一行数据筛选的时候都会去调用一次.

不过这里有一点: where 条件的布尔表达式的短路优化没有测试过.

[ 本帖最后由 nyfor 于 2008-9-8 15:48 编辑 ]


__________________
 
帮助别人,也在帮助自己!
Oracle Version 9.2.0
Oracle Version 10.2.0

看别人不顺眼是自己修养不够!
只看该作者    顶部
在线/呼叫 zhangfengh
老狐狸


精华贴数 3
个人空间 0
技术积分 14590 (79)
社区积分 7160 (220)
注册日期 2002-10-12
论坛徽章:123
现任管理团队成员生肖徽章2007版:鸡生肖徽章2007版:鸡生肖徽章2007版:鸡生肖徽章2007版:鸡生肖徽章2007版:鸡
生肖徽章:鸡生肖徽章:鸡生肖徽章:鸡生肖徽章:鸡生肖徽章:鸡生肖徽章:鸡

发表于 2008-9-8 15:47 
三思太过分了,我都进来两次了,也没去抢人家的沙发。

nyfor,我支持你,把三思的沙发和我这个回帖都删掉吧


__________________
=======================================
狐狸在这个世界上是凭借聪明而得到生存的,并且在这个世界上占有一席之地,学习狐狸的聪明,少走弯路。
=======================================
吸收别人的经验,使之成为自己的经验!
=======================================
只看该作者    顶部
离线 nyfor
Oracle Fan


来自 苏州
精华贴数 7
个人空间 0
技术积分 8319 (155)
社区积分 475 (1610)
注册日期 2002-1-6
论坛徽章:17
现任管理团队成员     
      

发表于 2008-9-8 15:59 
3.
SQL> exec pkg.reset_n;

PL/SQL procedure successfully completed
SQL> select pkg.inc_n from t
  2   order by pkg.inc_n;

     INC_N
----------
         1
         2
         3
SQL> select pkg.get_n from dual;

     GET_N
----------
         3
SQL> exec pkg.reset_n;

PL/SQL procedure successfully completed
SQL> select pkg.inc_n from t
  2   order by pkg.inc_n + 1;

     INC_N
----------
         2
         4
         6
SQL> select pkg.get_n from dual;

     GET_N
----------
         6

SQL> exec pkg.reset_n;

PL/SQL procedure successfully completed
SQL> select pkg.inc_n + 1 from t
  2   order by pkg.inc_n + 1;

PKG.INC_N+1
-----------
          2
          3
          4
SQL> select pkg.get_n from dual;

     GET_N
----------
         3

这里我们发现函数调用出现在 order by 子句中的情况是,
Oracle 会把 Order By 中的函数调用先执行, 实际上应该是把 order by 表达式的列作为 select 的虚拟列, 如果 select 列中的函数调用表达式和 Order By 中出现的完全一样, 则 select 中将不再调用函数, 而是直接用从 Order By 中的函数调用的结果作为 Select 的结果. 倘若不一致, 则还需再次调用函数.


__________________
 
帮助别人,也在帮助自己!
Oracle Version 9.2.0
Oracle Version 10.2.0

看别人不顺眼是自己修养不够!
只看该作者    顶部
离线 nyfor
Oracle Fan


来自 苏州
精华贴数 7
个人空间 0
技术积分 8319 (155)
社区积分 475 (1610)
注册日期 2002-1-6
论坛徽章:17
现任管理团队成员     
      

发表于 2008-9-8 16:11 
4.
SQL> exec pkg.reset_n;

PL/SQL procedure successfully completed
SQL> select pkg.inc_n from t
  2   group by pkg.inc_n;

     INC_N
----------
         1
         2
         3
SQL> select pkg.get_n from dual;

     GET_N
----------
         3

SQL>
SQL> exec pkg.reset_n;

PL/SQL procedure successfully completed
SQL> select pkg.inc_n+1 from t
  2   group by pkg.inc_n;

PKG.INC_N+1
-----------
          2
          3
          4
SQL> select pkg.get_n from dual;

     GET_N
----------
         3
SQL> exec pkg.reset_n;

PL/SQL procedure successfully completed
SQL> select pkg.inc_n from t
  2   group by pkg.inc_n+1;

     INC_N
----------
         4
         5
         6
SQL> select pkg.get_n from dual;

     GET_N
----------
         6

当函数出现在 Group By表达式中的时候, 也是优先计算 Group By表达式的函数, 如果select 列中的函数调用表达式与 Group By 完全一致, 或者Group By 中的函数调用表达式是select 列中函数表达式的一部分时, select 列中的函数将不再调用, 这一点与 Order By既类似, 又有一点点区别. 此外和 Order by 中出现的函数表达式不同的是, group by 是所有行的计算完, 再计算select 列.


__________________
 
帮助别人,也在帮助自己!
Oracle Version 9.2.0
Oracle Version 10.2.0

看别人不顺眼是自己修养不够!
只看该作者    顶部
离线 nyfor
Oracle Fan


来自 苏州
精华贴数 7
个人空间 0
技术积分 8319 (155)
社区积分 475 (1610)
注册日期 2002-1-6
论坛徽章:17
现任管理团队成员     
      

发表于 2008-9-8 16:23 
以上出现在 order by, group by 表达式中的函数调用与出现在 where 中的还有一个不同就是后者的函数调用表达式不论是否与表数据有关, 均对每一行数据都调用一次(注意这是筛选之后的每一行)

5.
SQL> exec pkg.reset_n;

PL/SQL procedure successfully completed
SQL> select pkg.inc_n from t where pkg.inc_n = 1;

     INC_N
----------
         2
         3
         4
SQL> select pkg.get_n from dual;

     GET_N
----------
         4

SQL>
SQL> exec pkg.reset_n;

PL/SQL procedure successfully completed
SQL> select pkg.inc_n from t where pkg.inc_n = 2;

     INC_N
----------
SQL> select pkg.get_n from dual;

     GET_N
----------
         1

   以上两条结果似乎不是我们所想象的.
SQL> exec pkg.reset_n;

PL/SQL procedure successfully completed
SQL> select  * from (select pkg.inc_n n from t ) where n = 1;

         N
----------
         2
         3
         4
SQL> select pkg.get_n from dual;

     GET_N
----------
         4
SQL> exec pkg.reset_n;

PL/SQL procedure successfully completed
SQL> select  * from (select pkg.inc_n n from t ) where n = 2;

         N
----------
SQL> select pkg.get_n from dual;

     GET_N
----------
         1

这里我们发现Oracle对于复杂的查询做优化后导致执行结果不是我们想要的.
对于前两个SQL, Oracle 的执行路径已经转换为后面两个了. 那么我们有什么办法来防止这种情况发生呢, 禁止外层where谓词push 到内部查询, 但是Oracle好像又没有这样的 hint 可用. 目前我是没有找到什么好办法来阻止这种错误结果的发生.

[ 本帖最后由 nyfor 于 2008-9-8 16:36 编辑 ]


__________________
 
帮助别人,也在帮助自己!
Oracle Version 9.2.0
Oracle Version 10.2.0

看别人不顺眼是自己修养不够!
只看该作者    顶部
离线 nyfor
Oracle Fan


来自 苏州
精华贴数 7
个人空间 0
技术积分 8319 (155)
社区积分 475 (1610)
注册日期 2002-1-6
论坛徽章:17
现任管理团队成员     
      

发表于 2008-9-8 16:25 
暂时就调研到这里, 当你在SQL中调用对于同样的输入参数(可能无参数)的函数而输出结果是不同值的情况下, 要注意函数在SQL的调用次数问题.


__________________
 
帮助别人,也在帮助自己!
Oracle Version 9.2.0
Oracle Version 10.2.0

看别人不顺眼是自己修养不够!
只看该作者    顶部
离线 nyfor
Oracle Fan


来自 苏州
精华贴数 7
个人空间 0
技术积分 8319 (155)
社区积分 475 (1610)
注册日期 2002-1-6
论坛徽章:17
现任管理团队成员     
      

发表于 2008-9-8 16:28 


QUOTE:
原帖由 zhangfengh 于 2008-9-8 15:47 发表
三思太过分了,我都进来两次了,也没去抢人家的沙发。

nyfor,我支持你,把三思的沙发和我这个回帖都删掉吧

呵呵, 没啥关系的


__________________
 
帮助别人,也在帮助自己!
Oracle Version 9.2.0
Oracle Version 10.2.0

看别人不顺眼是自己修养不够!
只看该作者    顶部
离线 smallnavy
平常心


精华贴数 1
个人空间 3
技术积分 2407 (664)
社区积分 3478 (409)
注册日期 2003-9-2
论坛徽章:55
生肖徽章2007版:龙2008北京奥运纪念徽章:摔跤生肖徽章:龙   
      

发表于 2008-9-8 16:31 
终于进来了,不容易啊。


__________________
竹仗芒鞋轻胜马,一蓑烟雨任平生
只看该作者    顶部
相关内容


CopyRight 1999-2006 itpub.net All Right Reserved.
北京皓辰广域网络信息技术有限公司. 版权所有
E-mail:Webmaster@itpub.net
京ICP证:010037号 联系我们 法律顾问