12
返回列表 发新帖
楼主: sqysl

[原创] <高性能SQL调优精要与案例解析>中10.4部分SQL改写代码

[复制链接]
论坛徽章:
8
2009新春纪念徽章
日期:2009-01-04 14:52:28祖国60周年纪念徽章
日期:2009-10-09 08:28:002010新春纪念徽章
日期:2010-03-01 11:07:24ITPUB9周年纪念徽章
日期:2010-10-08 09:32:25ITPUB十周年纪念徽章
日期:2011-11-01 16:23:262013年新春福章
日期:2013-02-25 14:51:24沸羊羊
日期:2015-03-04 14:51:522015年新春福章
日期:2015-03-06 11:57:31
11#
 楼主| 发表于 2021-4-6 09:11 | 只看该作者
newkid 发表于 2021-4-4 04:40
惭愧,我都没注意到闫老师在本版发了样章。确实写得比较粗糙了点,有很多内容没有展开。关于10.4.7那个OR改 ...

帖子有点久了,谢谢各位大师点评。

使用道具 举报

回复
论坛徽章:
407
紫蛋头
日期:2012-05-21 10:19:41迷宫蛋
日期:2012-06-06 16:02:49奥运会纪念徽章:足球
日期:2012-06-29 15:30:06奥运会纪念徽章:排球
日期:2012-07-10 21:24:24鲜花蛋
日期:2012-07-16 15:24:59奥运会纪念徽章:拳击
日期:2012-08-07 10:54:50奥运会纪念徽章:羽毛球
日期:2012-08-21 15:55:33奥运会纪念徽章:蹦床
日期:2012-08-21 21:09:51奥运会纪念徽章:篮球
日期:2012-08-24 10:29:11奥运会纪念徽章:体操
日期:2012-09-07 16:40:00
12#
发表于 2021-4-7 06:07 来自手机 | 只看该作者
没换工作,电脑换成arm64 CPU了

使用道具 举报

回复
论坛徽章:
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
13#
发表于 2021-4-7 08:22 来自手机 | 只看该作者
这两天我也会写个点评,欢迎讨论。

使用道具 举报

回复
论坛徽章:
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
14#
发表于 2021-4-7 22:14 | 只看该作者
10.4 SQL 语句改写

10.4.1 消除视图
改写前:
SQL> create view v_test as select * from tab1 where col1=...;
SQL> select * from tab2 t2,tab3 t3,v_test v where t2.col1=t3.col1 and t3.col1=v.col1 and v.col2=...;
改写后:
SQL> select * from tab2 t2,tab3 t3,tab1 t1
   where t2.col1=t3.col1 and t3.col1=t1.col1 and t1.col2=... and t1.col1=...;

(点评:这样做的好处在哪里?一般来说,定义视图是为了重用代码,隐藏复杂逻辑。这个例子改写后代码变多了,性能上也不见得提升。在12C已经有一个系统功能可以自动替你做这样的事:DBMS_UTILITY.expand_sql_text)

10.4.2 标量子查询改为外连接
改写前:
SQL> select t1.col1,
           ( select col2
                from ( select t2.col1,sum(t2.col2) col2 from tab2 t2 group by t2.col1 ) t3
                where t3.col1=t1.col2
           ) col2_sum
     from tab1 t1
    where t1.col3=...;
改写后:
SQL> select t1.col1,t3.col2 col2_sum
     from tab1 t1, ( select t2.col1,sum(t2.col2) col2 from tab2 t2 group by t2.col1 ) t3
     where t1.col2=t3.col1(+) and t1.col3=...;

(点评:标量子查询好不好,取决于调用次数有多少以及执行一次代价有多少。如果外层结果行数不多,或者驱动子查询所用的值没有多少个(这样可以利用行间缓存),每次查询都能高效利用索引,那么有时候比改写成外连接还更好。
)

10.4.3 updatepdate改为改为merge into
改写前:
SQL> update tab1 t1 set col2= ( select col2 from tab2 t2 where t1.col1=t2.col1 ) where t1.col3=...;
改写后:
SQL> merge into tab1 t1 using tab2 t2 on ( t1.col1=t2.col1 ) when matched then update set t1.col2=t2.col2 where t1.col3=...;

(点评:注意改写之前,假设T2中找不到相应数据,那么T1.COL2是会被修改成NULL的。所以不能够直接 USING TAB2,而是要USING (select ... from tab1 t1 left join tab2 t2 on t1.col1=t2.col1 where t1.col3=...)
如果改写前WHERE条件里面有EXISTS条件保证T2一定能找到数据,那么可以直接 USING TAB2。
)

10.4.4 正确使用分析函数
改写前:
SQL> select t1.col1 from tab1 t1 where t1.col2> ( select avg(col2) from tab1 t2 where t2.col3=t1.col3 );
改写后:
SQL> select t2.col1 from ( select t1.col1,t1.col2,avg(t1.col2) over(partition by c3) avg_col2 from tab1 t1 ) t2
where t2.col2>t2.avg_col2;

10.4.5 with as去除多次扫描
改写前:
SQL> select t1.col1,t1.col2
     from tab1 t1
     where t1.col3 in ( select col2 from tab2 where col1=... )
           and t1.col4 in  ( select col3 from tab2 where col1=... and col2>... )
           and t1.col5 in ( select col4 from tab2 where col1=... and col2>… and col3<... );

改写后:
SQL> with t1 as ( select col2,col3,col4 from tab2 where col1=... )
        , t2 as ( select col3 from t1 where col2>... )
        , t3 as ( select col4 from t1 where col2>... and col3<... )
   select distint t4.col1,t4.col2
    from tab1 t4,t1,t2,t3
     where t4.col3=t1.col2 and t4.col4=t2.col3 and t4.col5=t3.col4;

(点评:这个改写是不对的,加上distinct之后和原来不等价了。原查询可能返回重复的col1,col2结果。正确的改法是t1,t2,t3分别加上distinct。但是t1为了照顾t2和t3加上了额外的列,distinct也不行,要在join之前做。
with t1 as ( select col2,col3,col4 from tab2 where col1=... )
        , t2 as ( select distinct col3 from t1 where col2>... )
        , t3 as ( select distinct col4 from t1 where col2>... and col3<... )
   select t4.col1,t4.col2
    from tab1 t4,(select distinct col2 from t1) t1,t2,t3
     where t4.col3=t1.col2 and t4.col4=t2.col3 and t4.col5=t3.col4;
)

10.4.6 union 改为or
改写前:
SQL> select t1.col1
    from tab1 t1
    where t1.col2=...
          and t1.col3 in ( select col1 from tab2 t2 where col2=... union select col1 from tab2 t3 where col3>... );

改写后:
SQL> select t1.col1
     from tab1 t1
        where t1.col2=...
             and t1.col3 in ( select col1 from tab2 t2 where col2=... or col3>...);

10.4.7 or改为union
改写前:
SQL> select t1.col1
       from tab1 t1
       where t1.col2=... and t1.col3 in ( select col1 from tab2 t2 where col2=... or col3>...);

改写后:
SQL> select t1.col1
      from tab1 t1
      where t1.col2=... and t1.col3 in ( select col1 from tab2 t2 where col2=... union select col1 from tab2 t3 where col3>... );

(点评:此处改写的是 IN 子查询里面的代码,这样做是没有问题,否则要注意UNION是会对整个结果集去重的,可能和OR写法不等价)

10.4.8 in改为join
改写前:
SQL> select t1.col1 from tab1 t1 where t1.col2 in ( select col1 from tab2 where col2=... and col3>... );

改写后:
SQL> select distinct t1.col1 from tab1 t1,tab2 t2 where t1.col2=t2.col1 and col2=... and col3>...;
(点评:类似10.4.5,distinct不能放在这里,而是应该放在t2。现在的优化器会自动做这类改写。
select t1.col1
from tab1 t1,
    ( select distinct col1 from tab2 where col2=... and col3>... ) t2
where t1.col2=t2.col1
)

10.4.9 in改为改为exists
改写前:
SQL> select t1.col1 from tab1 t1 where t1.col2 in ( select col1 from tab2 where col2=... );

改写后:
SQL> select t1.col1 from tab1 t1 where exists ( select 1 from tab2 t2 where t1.col2=t2.col1 and t2.col2=... );

10.4.10
not in改为改为not exists
改写前:
SQL> select col1
     from tab1
     where col2=...
          and col3 not in ( select col1 from tab2 where col2=… and col1 is not null);

改写后:
SQL> select col1
      from tab1 t1
      where col2=...
            and not exists ( select 1 from tab2 t2 where col2=... and t1.col3=t2.col1)
            and col3 is not null;

10.4.11 not exists改为not in
改写前:
SQL> select col1
      from tab1 t1
      where col2=...
            and not exists ( select 1 from tab2 t2 where col2=... and t1.col3=t2.col1)
            and col3 is not null;

改写后:
SQL> select col1
       from tab1
       where col2=...
             and col3 not in ( select col1 from tab2 where col2=… and col1 is not null);

10.4.12 exists改为join
改写前:
SQL> select * from tab1 t1 where col2=... and exists ( select * from tab2 t2 where t1.col3=t2.col1 and t2.col2=... );

改写后:
SQL> select distinct t1.*
     from tab1 t1,tab2 t2 where t1.col2=... and t1.col3=t2.col1 and t2.col2=...;

(点评:因为是select *, 假设t1有唯一键,那么加上distinct是没问题的。但是在一些情况下,效率上不如在 join 之前做distinct:
select t1.*
     from tab1 t1,
          (select distinct col1 from tab2 where col2=...) t2
   where t1.col2=... and t1.col3=t2.col1;
)

10.4.13 not exists改为join
改写前:
SQL> select col1 from tab1 t1 where col2=... and not exists
     ( select 1 from tab2 t2 where col1=... and t1.col3=t2.col2 );

改写后:
SQL> select t1.col1
     from tab1 t1,tab2 t2
     where t1.col2=... and t2.col1=... and t1.col3=t2.col2(+) and t2.col2 is null;

(点评:严谨一点应该说改为outer join
这个改法是错的,最后有t2.col1=... 这个条件,已经变成inner join了。应该写t2.col1(+)=...
推荐用ansi join语法:
select t1.col1
     from tab1 t1 left join tab2 t2 on t2.col1=... and t1.col3=t2.col2
     where t1.col2=... and t2.col2 is null;
)

10.4.14 join改为exists
改写前:
SQL> select distinct t1.col1 from tab1 t1,tab2 t2 where t1.col2=... and t2.col1=... and t1.col3=t2.col2;

改写后:
SQL> select distinct col1
       from tab1
       where col2=... and exists ( select 1 from tab2 t2 where t2.col1=... and t1.col3=t2.col2 );

10.4.15 join改为not exists
改写前:
SQL> select t1.col1 from tab1 t1,tab2 t2 where t1.col2=... and t2.col1=... and t1.col3=t2.col2(+) and t2.col2 is null;

改写后:
SQL> select col1 from tab1 t1 where col2=... and not exists ( select 1 from tab2 t2 where col1=... and t1.col3=t2.col2 );

(点评:参见10.4.13)

10.4.16 改写为集合运算符
改写前:
SQL> select col1
     from tab1
     where col2=... and col3 in ( select col1 from tab2 where col2=... )
          and col3 in ( select col1 from tab3 where col2... and col3>... );

改写后:
SQL> select col1
    from tab1
    where col2=...
          and col3 in ( select col1 from tab2 where col2=... intersect select col1 from tab3 where col2=... and col3>... );

(点评:把in子查询的intersect改成inner join可以提高效率)


使用道具 举报

回复

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

本版积分规则 发表回复

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