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

标题: [精华] 很少受关注的SQL中一些函数调用秘密
  本主题由 System 于 2008-9-11 05:00 解除限时置顶 
在线/呼叫 newkid
资深新手
老程序员


来自 银河系
精华贴数 2
个人空间 0
技术积分 2778 (579)
社区积分 0 (121048)
注册日期 2004-6-26
论坛徽章:5
生肖徽章2007版:马生肖徽章2007版:鸡2008北京奥运纪念徽章:垒球2008北京奥运纪念徽章:羽毛球  
      

发表于 2008-9-9 00:53 
用大一点的数据试验:

DELETE t;
INSERT INTO t (SELECT MOD(LEVEL,10) FROM DUAL CONNECT BY LEVEL<=1000);

SELECT COUNT(DISTINCT id) FROM T;

COUNT(DISTINCTID)
-----------------
              10


exec pkg.reset_n;

select pkg.inc2_n(id) from t;

select pkg.get_n from dual;

    GET_N
---------
     1000           ---- 每行调一次


改用SCALAR SUBQUERY:

exec pkg.reset_n;

select (SELECT pkg.inc2_n(id) FROM DUAL) from t;

select pkg.get_n from dual;

     GET_N
----------
        10        -------- 每个值调一次

改用重复率低一点的数据:
DELETE t;
INSERT INTO t (SELECT MOD(LEVEL,500) FROM DUAL CONNECT BY LEVEL<=1000);

SELECT COUNT(DISTINCT id) FROM T;

COUNT(DISTINCTID)
-----------------
             500


exec pkg.reset_n;

select pkg.inc2_n(id) from t;

select pkg.get_n from dual;

    GET_N
---------
     1000         ------ 还是1000次


改用SCALAR SUBQUERY:

exec pkg.reset_n;

select (SELECT pkg.inc2_n(id) FROM DUAL) from t;

select pkg.get_n from dual;

     GET_N
----------
       608         --- 不是500次了


__________________
只看该作者    顶部
离线 nyfor
Oracle Fan


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

发表于 2008-9-9 09:01 
newkid 的研究更深入呀, 佩服, 给朵


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

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


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

发表于 2008-9-9 09:19 


QUOTE:
原帖由 nyfor 于 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 可用. 目前我是没有找到什么好办法来阻止这种错误结果的发生.
这个简单:select  * from (select pkg.inc_n n,rownum rn from t ) where n = 1;即可





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


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

发表于 2008-9-9 09:35 
select  * from (select pkg.inc_n n,rownum rn from t ) where n = 1;
真是很奇怪啊, 加了个 Rownum 列它就正确了.

Oracle 好深奥哦...

个人觉得: 函数调用次数的不确定性, 以及某些SQL产生我们不期望的结果, 都归结于Oracle的优化器的优化原理.


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

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


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

发表于 2008-9-9 09:41 
newkid 讲的 DETERMINISTIC 函数, 以及函数返回值的 cache 策略, 其实都不太重要,
因为 DETERMINISTIC 函数的调用次数不确定并不会影响SQL查询的结果.
而非 DETERMINISTIC 函数就麻烦了, 极有可能影响SQL查询的结果.


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

看别人不顺眼是自己修养不够!
只看该作者    顶部
在线/呼叫 newkid
资深新手
老程序员


来自 银河系
精华贴数 2
个人空间 0
技术积分 2778 (579)
社区积分 0 (121048)
注册日期 2004-6-26
论坛徽章:5
生肖徽章2007版:马生肖徽章2007版:鸡2008北京奥运纪念徽章:垒球2008北京奥运纪念徽章:羽毛球  
      

发表于 2008-9-9 10:17 
呵呵,我是在你启发下做了些试验,知其然不知其所以然。
不知你们看了我#18的贴没有?用ROWNUM的效果我也是知道的,这是为了防止PUSH谓词,但我不知道NO_MERGE HINT怎么不起作用呢?你们用NO_MERGE试过没有?
要说这个研究成果,我认为有以下几点:
1.不要依赖于函数调用次数。在函数里不要有修改上下文相关的操作。
2.第1点做到之后,我们就要尽量减少函数调用,提高效率。
2.a 在你理解了DETERMINISTIC 函数的前提下,尽量用DETERMINISTIC 函数。典型的应用例子:代码转名称,名称转代码。
2.b 在非DETERMINISTIC 函数时,也要尽量让ORACLE CACHE住你的函数结果。可用技巧就是SCALAR SUBQUERY.

nyfor 你认为1000次调用和10次调用没有区别吗?哪个效率高?尽管结果都是一样的。

请继续讨论哈,明天我再来。


__________________
只看该作者    顶部
离线 dreamroot
一般会员



精华贴数 0
个人空间 0
技术积分 358 (5544)
社区积分 0 (233585)
注册日期 2004-12-22
论坛徽章:0
      
      

发表于 2008-9-9 10:24 
虽然我是菜鸟,但感觉你的研究很无聊和没有必要性。一般很少有人会用dbms_random.value进行随机查询的。而且在dbms_random.value<dbms_random.value.这样的where条件,我想大概没有人会用到的,对于一个case来说,总有其明确的需求,则同样带来了明确的where条件。不会用dbms_random.value <dbms_random.value. 作为无聊的条件的. oracle的聚组函数其功用是固定的,唯一变化的只是随查询的数据的变化而变化,其值也只会返回唯一值.用户需求的也就是可以确定的期望值.在程序中如果多次调用,建议每次在调用之前给个初值,则无论调用多少次都不会带来错误的结果的.
个人浅见,仅供参考.


__________________
no pains,no gains.走自已的路﹐讓別人去說吧﹗
只看该作者    顶部
离线 nyfor
Oracle Fan


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

发表于 2008-9-9 10:40 


QUOTE:
原帖由 newkid 于 2008-9-9 10:17 发表
呵呵,我是在你启发下做了些试验,知其然不知其所以然。
不知你们看了我#18的贴没有?用ROWNUM的效果我也是知道的,这是为了防止PUSH谓词,但我不知道NO_MERGE HINT怎么不起作用呢?你们用NO_MERGE试过没有?
要说这个研究成果,我认为有以下几点:
1.不要依赖于函数调用次数。在函数里不要有修改上下文相关的操作。
2.第1点做到之后,我们就要尽量减少函数调用,提高效率。
2.a 在你理解了DETERMINISTIC 函数的前提下,尽量用DETERMINISTIC 函数。典型的应用例子:代码转名称,名称转代码。
2.b 在非DETERMINISTIC 函数时,也要尽量让ORACLE CACHE住你的函数结果。可用技巧就是SCALAR SUBQUERY.

nyfor 你认为1000次调用和10次调用没有区别吗?哪个效率高?尽管结果都是一样的。

请继续讨论哈,明天我再来。

我看了你的那个帖子的. 也看到了使用 rownum 后的效果, 只是后来三思再次提起, 我就回复了一下.
no_merge 之前我没试过, 刚试了, 真的不行.
1000次调用和10次调用当然有性能上的区别, 我说的不太重要的原因是说 不会影响到结果的正确性, 也就是说至少不会让我们的代码出现BUG.


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

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


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

发表于 2008-9-9 10:43 
to:nf
不奇怪,rownum固化结果集,不好使才叫奇怪~~

to:newkid
no_merge不会有效,原因很简单,看看该语句的执行计划就明白了~


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


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

发表于 2008-9-9 11:01 
DETERMINISTIC 函数特性在 Oracle 10G 才得以实现.
9I 中还无效, 虽然可以定义这样的函数.


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

看别人不顺眼是自己修养不够!
只看该作者    顶部
相关内容


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