楼主: nyfor

[精华] 很少受关注的SQL中一些函数调用秘密

[复制链接]
论坛徽章:
87
2015年新春福章
日期:2015-03-06 11:58:182010广州亚运会纪念徽章:轮滑
日期:2010-09-23 17:19:212010年世界杯参赛球队:乌拉圭
日期:2010-07-14 17:54:242010年世界杯参赛球队:美国
日期:2010-06-30 13:13:582010年世界杯参赛球队:墨西哥
日期:2010-06-25 12:49:452010年世界杯参赛球队:墨西哥
日期:2010-04-05 10:23:502010新春纪念徽章
日期:2010-03-01 11:06:232010新春纪念徽章
日期:2010-01-04 08:33:08生肖徽章2007版:龙
日期:2009-11-12 16:31:13参与WIN7挑战赛纪念
日期:2009-11-09 11:50:09
11#
发表于 2008-9-8 16:38 | 只看该作者
研究,研究

使用道具 举报

回复
论坛徽章:
242
蛋疼蛋
日期:2013-01-29 11:27:262013年新春福章
日期:2013-02-25 14:51:24ITPUB社区12周年站庆徽章
日期:2013-10-08 14:56:08
12#
发表于 2008-9-8 16:49 | 只看该作者
进来学习!

使用道具 举报

回复
论坛徽章:
5
奥运会纪念徽章:跳水
日期:2008-08-26 11:49:50奥运会纪念徽章:跳水
日期:2008-08-28 21:31:01奥运会纪念徽章:足球
日期:2008-09-04 23:00:00ITPUB9周年纪念徽章
日期:2010-10-08 09:31:22优秀写手
日期:2013-12-18 09:29:11
13#
发表于 2008-9-8 17:16 | 只看该作者
看的不是很明白,晚上回去再看

使用道具 举报

回复
论坛徽章:
40
授权会员
日期:2009-03-04 17:06:25最佳人气徽章
日期:2013-03-19 17:24:25SQL极客
日期:2013-12-09 14:13:35优秀写手
日期:2013-12-18 09:29:09ITPUB元老
日期:2015-03-04 13:33:34白羊座
日期:2016-03-11 13:49:34乌索普
日期:2017-11-17 11:40:00
14#
发表于 2008-9-8 17:57 | 只看该作者
先留个名  明天上班了慢慢看

使用道具 举报

回复
论坛徽章:
1088
金色在线徽章
日期:2007-04-25 04:02:08金色在线徽章
日期:2007-06-29 04:02:43金色在线徽章
日期:2007-03-11 04:02:02在线时间
日期:2007-04-11 04:01:02在线时间
日期:2007-04-12 04:01:02在线时间
日期:2007-03-07 04:01:022008版在线时间
日期:2010-05-01 00:01:152008版在线时间
日期:2011-05-01 00:01:342008版在线时间
日期:2008-06-03 11:59:43ITPUB年度最佳技术原创精华奖
日期:2013-03-22 13:18:30
15#
发表于 2008-9-8 18:09 | 只看该作者
的确不容易,终于进来踩一下了!

使用道具 举报

回复
论坛徽章:
520
奥运会纪念徽章:垒球
日期:2008-09-15 01:28:12生肖徽章2007版:鸡
日期:2008-11-17 23:40:58生肖徽章2007版:马
日期:2008-11-18 05:09:48数据库板块每日发贴之星
日期:2008-11-29 01:01:02数据库板块每日发贴之星
日期:2008-12-05 01:01:03生肖徽章2007版:虎
日期:2008-12-10 07:47:462009新春纪念徽章
日期:2009-01-04 14:52:28数据库板块每日发贴之星
日期:2009-02-08 01:01:03生肖徽章2007版:蛇
日期:2009-03-09 22:18:532009日食纪念
日期:2009-07-22 09:30:00
16#
发表于 2008-9-8 22:51 | 只看该作者
呵呵,内部装修结束了?支持一下楼主的钻研精神。

关于#7, 可以用这个技巧:

SQL> exec pkg.reset_n;

PL/SQL procedure successfully completed.

SQL> select  * from (select pkg.inc_n n from t WHERE ROWNUM>0) where n = 2;

         N
----------
         2

照理说 NO_MERGE HINT 也可以,但我没试出来,不知道是什么道理。

参数不变时返回值也不变的函数叫 DETERMINISTIC 函数,在声明时可以加这个关键字,ORACLE会CACHE返回值。

另一个让ORACLE CACHE 住函数的方法是用SCALAR SUBQUERY:

SELECT ID, (SELECT DBMS_RANDOM.VALUE FROM DUAL) FROM T;

你会发现随机函数只发生了一次。

如果CBO认为每次子查询不一样,它还是会执行多次:

SELECT ID, (SELECT DBMS_RANDOM.VALUE FROM DUAL WHERE ID>0) FROM T;

函数调用次数是很难预测的,影响因素很多。到11G,函数的CACHE策略又发生了变化,不过我没有环境没法测。

等会有空我也贴几个有趣的例子上来。

使用道具 举报

回复
论坛徽章:
9607
土豪章
日期:2013-12-31 14:11:39土豪章
日期:2013-12-31 14:11:39阿森纳
日期:2013-06-03 17:00:31阿森纳
日期:2013-10-11 09:27:58法拉利
日期:2013-12-27 15:20:30林肯
日期:2013-12-27 15:19:09法拉利
日期:2013-12-27 15:20:30法拉利
日期:2013-12-27 15:20:30法拉利
日期:2013-12-27 15:20:30法拉利
日期:2013-12-27 15:20:30
17#
发表于 2008-9-8 23:12 | 只看该作者
mark

使用道具 举报

回复
论坛徽章:
520
奥运会纪念徽章:垒球
日期:2008-09-15 01:28:12生肖徽章2007版:鸡
日期:2008-11-17 23:40:58生肖徽章2007版:马
日期:2008-11-18 05:09:48数据库板块每日发贴之星
日期:2008-11-29 01:01:02数据库板块每日发贴之星
日期:2008-12-05 01:01:03生肖徽章2007版:虎
日期:2008-12-10 07:47:462009新春纪念徽章
日期:2009-01-04 14:52:28数据库板块每日发贴之星
日期:2009-02-08 01:01:03生肖徽章2007版:蛇
日期:2009-03-09 22:18:532009日食纪念
日期:2009-07-22 09:30:00
18#
发表于 2008-9-9 00:05 | 只看该作者
原帖由 nyfor 于 2008-9-8 16:23 发表
以上出现在 order by, group by 表达式中的函数调用与出现在 where 中的还有一个不同就是后者的函数调用表达式不论是否与表数据有关, 均对每一行数据都调用一次(注意这是筛选之后的每一行)


在WHERE中的函数调用是会受索引影响的。
我先稍微改造一下你的PACKAGE, 增加一个inc2_n:

create or replace package pkg
is
  function get_n return number;
  function inc_n return number;
  procedure reset_n;

  function inc2_n(p_n in number) return number;

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;

  function inc2_n(p_n in number) return number
  is
  begin
    g_n := g_n + 1;
    return p_n;
  end;

begin
  g_n := 0;
end;
/


开始试验:
exec pkg.reset_n;

select * from t where id=pkg.inc2_n(2);

select pkg.get_n from dual;

     GET_N
----------
         3          ----- 全表扫描,执行了三次
         

--- 增加一个索引再做同样试验
create index t_x on t(id);

exec pkg.reset_n;

select * from t where id=pkg.inc2_n(2);

select pkg.get_n from dual;

     GET_N
----------
         2       ----- 走了索引,执行了两次,不知道为什么不是一次

使用道具 举报

回复
论坛徽章:
520
奥运会纪念徽章:垒球
日期:2008-09-15 01:28:12生肖徽章2007版:鸡
日期:2008-11-17 23:40:58生肖徽章2007版:马
日期:2008-11-18 05:09:48数据库板块每日发贴之星
日期:2008-11-29 01:01:02数据库板块每日发贴之星
日期:2008-12-05 01:01:03生肖徽章2007版:虎
日期:2008-12-10 07:47:462009新春纪念徽章
日期:2009-01-04 14:52:28数据库板块每日发贴之星
日期:2009-02-08 01:01:03生肖徽章2007版:蛇
日期:2009-03-09 22:18:532009日食纪念
日期:2009-07-22 09:30:00
19#
发表于 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次了

使用道具 举报

回复
论坛徽章:
69
生肖徽章2007版:羊
日期:2008-11-14 14:42:19复活蛋
日期:2011-08-06 08:59:05ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:542012新春纪念徽章
日期:2012-02-13 15:13:202012新春纪念徽章
日期:2012-02-13 15:13:202012新春纪念徽章
日期:2012-02-13 15:13:202012新春纪念徽章
日期:2012-02-13 15:13:202012新春纪念徽章
日期:2012-02-13 15:13:20版主4段
日期:2012-05-15 15:24:11
20#
 楼主| 发表于 2008-9-9 09:01 | 只看该作者
newkid 的研究更深入呀, 佩服, 给朵

使用道具 举报

回复

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

TOP技术积分榜 社区积分榜 徽章 团队 统计 知识索引树 积分竞拍 文本模式 帮助
  ITPUB首页 | ITPUB论坛 | 数据库技术 | 企业信息化 | 开发技术 | 微软技术 | 软件工程与项目管理 | IBM技术园地 | 行业纵向讨论 | IT招聘 | IT文档
  ChinaUnix | ChinaUnix博客 | ChinaUnix论坛
CopyRight 1999-2011 itpub.net All Right Reserved. 北京盛拓优讯信息技术有限公司版权所有 联系我们 未成年人举报专区 
京ICP备16024965号-8  北京市公安局海淀分局网监中心备案编号:11010802021510 广播电视节目制作经营许可证:编号(京)字第1149号
  
快速回复 返回顶部 返回列表