第五章 SQL优化大法

<Oracle优化新常态>第一章

《Oracle优化新常态》第二章强拆(1)

《Oracle优化新常态》第二章强拆(2)

<Oracle优化新常态> 第三章 三大配置

<Oracle优化新常态>第四章 分库分表

<Oracle优化新常态> 第五章 急诊法

<Oracle优化新常态> 第五章 SQL优化大法

SQL是应用程序发给数据库工作的基本单位!所以优化SQL是优化的重中之重。也是立杆见影,成效快,短平快,易出GDP成绩的。但是SQL优化又是个深奥,高深的技术,要充分理解数据库运行机制,也要了解业务特性,以及PL/SQL开发技术。是业务+开发+原理的三位一体!不是一般运维DBA能胜能的,不是会搭建个数据库,搭建个高可用DATAGUARD,搭建个集群RAC,搭建个数据同步OGG的运维DBA就可以优化的。也不是运维工程师能胜能的,虽然你会索引大法。如今SQL是有开发人员写的,自然他们写得好才叫奇迹,写得很垃圾那叫做正常。开发人员工作业绩就是出GDP,而不是出性能。他们连代码质量都无法保证,都要交给测试人员去完成,而且测试人员的数量都是开发人员的两倍。至于性能我就呵呵了! 你说对开发人员做培训,呵呵! 说深了他们又不懂,说潜了他们傲娇觉得鄙视你!

那我就说简单易用易懂而且见效快的SQL优化大法!

第一 优化大法 美图秀秀法

只要是个女人拍照照,必然要美化下自己,PS下自己,何况是个男人啊。所以作为男人写的SQL,也是要爱护自己的面子的,如今大男人主义盛行的年代,虽然IT男字写不好,可代码敲得也要漂亮的喊不要 不要啊!

其一 一段赏心悦目的SQL代码,会给人一种心情愉悦之感。

其二 可以很快判断出语法问题

其三 可以很快地了解到SQL性能问题所在

其四 见过丑的你才知道什么是美好的

SELECT GW.GW_NO, GW.GW_MER_NO, GW.GW_STATUS, (SELECT COUNT(1) FROM CCPS_TRADERECORD C WHERE C.TR_STATUS = 1 AND C.TR_DATETIME >= TO_DATE(:1, 'yyyy-mm-dd') AND C.TR_DATETIME <TO_DATE(:2, 'yyyy-mm-dd') AND GW.GW_NO = C.TR_GW_NO AND gw.gw_mer_no = c.tr_mer_no AND C.TR_CARDTYPE = :3 ) AS LAST_COUNT, (SELECT COUNT(1) FROM CCPS_TRADERECORD D WHERE D.TR_STATUS = 1 AND D.TR_DATETIME >= TO_DATE(:4, 'yyyy-mm-dd') AND D.TR_DATETIME < TO_DATE(:5, 'yyyy-mm-dd') AND GW.GW_NO = D.TR_GW_NO AND gw.gw_mer_no =

d.tr_mer_no AND D.TR_CARDTYPE = :6 ) TOTAL_CNT, (SELECT COUNT(DISTINCT UN.UP_TR_NO) FROM CCPS_UNNORMAL_PROCESS UN INNER JOIN CCPS_TRADERECORD UT ON UT.TR_NO = UN.UP_TR_NO WHERE UN.UP_CPD_TIME >= TO_DATE(:7, 'yyyy-mm-dd') AND UN.UP_CPD_TIME < TO_DATE(:8, 'yyyy-mm-dd') AND UN.UP_TYPE IN (2, 6) AND UT.TR_CARDTYPE = :9 AND GW.GW_NO = UT.TR_GW_NO AND GW.GW_MER_NO = UT.Tr_Mer_No ) PROTEST_CNT FROM CCPS_GATEWAY GW WHERE GW.GW_STATUS IN (-1, 1)

用PL/SQL格式化工具后这样

SELECT GW.GW_NO,

   GW.GW_MER_NO,

   GW.GW_STATUS,

   (SELECT COUNT(1)

      FROM CCPS_TRADERECORD C

     WHERE C.TR_STATUS = 1

       AND C.TR_DATETIME >= TO_DATE(:1, 'yyyy-mm-dd')

       AND C.TR_DATETIME < TO_DATE(:2, 'yyyy-mm-dd')

       AND GW.GW_NO = C.TR_GW_NO

       AND gw.gw_mer_no = c.tr_mer_no

       AND C.TR_CARDTYPE = :3) AS LAST_COUNT,

   (SELECT COUNT(1)

      FROM CCPS_TRADERECORD D

     WHERE D.TR_STATUS = 1

       AND D.TR_DATETIME >= TO_DATE(:4, 'yyyy-mm-dd')

       AND D.TR_DATETIME < TO_DATE(:5, 'yyyy-mm-dd')

       AND GW.GW_NO = D.TR_GW_NO

       AND gw.gw_mer_no = d.tr_mer_no

       AND D.TR_CARDTYPE = :6) TOTAL_CNT,

   (SELECT COUNT(DISTINCT UN.UP_TR_NO)

      FROM CCPS_UNNORMAL_PROCESS UN

     INNER JOIN CCPS_TRADERECORD UT

        ON UT.TR_NO = UN.UP_TR_NO

     WHERE UN.UP_CPD_TIME >= TO_DATE(:7, 'yyyy-mm-dd')

       AND UN.UP_CPD_TIME < TO_DATE(:8, 'yyyy-mm-dd')

       AND UN.UP_TYPE IN (2, 6)

       AND UT.TR_CARDTYPE = :9

       AND GW.GW_NO = UT.TR_GW_NO

       AND GW.GW_MER_NO = UT.Tr_Mer_No) PROTEST_CNT

FROM CCPS_GATEWAY GW

WHERE GW.GW_STATUS IN (-1, 1)

当还是不漂亮,只是不丑了而已,人工化妆下。

SELECT GW.GW_NO,

   GW.GW_MER_NO,

   GW.GW_STATUS,

   (

    SELECT COUNT(1)

      FROM CCPS_TRADERECORD C

     WHERE C.TR_STATUS = 1

       AND C.TR_DATETIME >= TO_DATE(:1, 'yyyy-mm-dd')

       AND C.TR_DATETIME < TO_DATE(:2, 'yyyy-mm-dd')

       AND GW.GW_NO = C.TR_GW_NO

       AND gw.gw_mer_no = c.tr_mer_no

       AND C.TR_CARDTYPE = :3

    ) AS LAST_COUNT,

   (

    SELECT COUNT(1)

      FROM CCPS_TRADERECORD D

     WHERE D.TR_STATUS = 1

       AND D.TR_DATETIME >= TO_DATE(:4, 'yyyy-mm-dd')

       AND D.TR_DATETIME < TO_DATE(:5, 'yyyy-mm-dd')

       AND GW.GW_NO = D.TR_GW_NO

       AND gw.gw_mer_no = d.tr_mer_no

       AND D.TR_CARDTYPE = :6

   ) TOTAL_CNT,

   (

   SELECT COUNT(DISTINCT UN.UP_TR_NO)

      FROM CCPS_UNNORMAL_PROCESS UN

     INNER JOIN CCPS_TRADERECORD UT     ON UT.TR_NO = UN.UP_TR_NO

     WHERE UN.UP_CPD_TIME >= TO_DATE(:7, 'yyyy-mm-dd')

       AND UN.UP_CPD_TIME < TO_DATE(:8, 'yyyy-mm-dd')

       AND UN.UP_TYPE IN (2, 6)

       AND UT.TR_CARDTYPE = :9

       AND GW.GW_NO = UT.TR_GW_NO

       AND GW.GW_MER_NO = UT.Tr_Mer_No

     ) PROTEST_CNT

FROM CCPS_GATEWAY GW

WHERE GW.GW_STATUS IN (-1, 1);

再来个丑的

select name,

   value,

   unit,

   (case

     when unit = 'bytes' then

      (value / 1024 / 1024 / 1024)

     else

      NULL

   end) as UNIT_GB

from V $PGASTAT;

select name,

     value,

     unit,

case when unit = 'bytes' then (value/1024/1024/1024, 3) else NULL end as UNIT_GB

from V $PGASTAT;

人工美化原则:

1 select 字段 如果存在计算字段的话,或者对字段额外处理,则每个字段占一行

2 SQL 列长度由原来的80列改成120列,毕竟如今都是宽屏时代了

3 子查询的小挂号单独占一行,以显示出这里有个子查询

4 CASE 一般都是处理SELECT字段的,不能分成多行,必须独占一行,拥挤在一起,因为它们是个逻辑整体,拆多行,大脑无法把上下连在一起。

5 小挂号不要乱用,如上 CASE 前面加个小挂号 (case... ) as 小挂号是提高内部的运算优先级的。加不加小挂号都无法改变的优先级,那就不要加,会干扰视觉。

6 关键字都要右对齐

7 SQL语句应该小写,大写无法快速区分单词

8 请使用新的表连接法 inner join left join

9 inner join on x.id=y.id on与join 同在一行

10 FROM 后跟的是主表,紧接着是inner join 表,然后是 left join 最后是其他的

11 select 每个字段最好前面有表名前缀

12 尊重习惯把条件字段左边放 and (sysdate - :2 / (24 * 60)) >= trd.tr_datetime

select a.id,a.name,b.sex,b.btherday,c.country,c.address,d.email

from a

inner join b on a.id=b.id

left join c on a.id=c.id

right join d on a.id=d.id

where 1=1

and  a.name='shark'


第二优化大法 条件字段不处理法

所谓条件字段不处理法 是说不对WHERE 后面的字段做任何处理。

select trd.tr_no,

   trd.tr_reference,

   trd.tr_status,

   trd.tr_paystarttime,

   trd.tr_datetime,

   trd.tr_bankcurrency,

   trd.tr_bankamout,

   trd.tr_cha_code,

   ch.cha_merno,

   ch.cha_vpc_accesscode,

   ch.cha_secure_secret,

   trd.TR_SF_DATA,

   trd.TR_CARDTYPE

from ccps_traderecord trd

left join ccps_channel ch on trd.tr_cha_code = ch.cha_code

where trd.TR_MER_NO != :1

and trd.tr_checked = 0

and trd.tr_status != -2

and (sysdate - :2 / (24 * 60)) >= trd.tr_datetime

and upper(trd.tr_bank_code) = :3

and rownum <= :4

order by trd.tr_id asc

这语句的绿色部分对字段做了UPDATE处理。任务对字段做处理都无法利用上该字段的索引。

第三优化大法 隐身大法

所谓隐身 是指默认的优先级 比如字符和日期之间的转换

and a.name=12360

name 是字符类型的字段,而输入的是数字,默认情况下会对name做隐身转换。

第四优化大法 吸星大法

吸星 是指 星号 *

一般情况下 大家都喜欢 select from a 很省事 快捷,开发人员的最爱。至于会导致什么后果? 我就不说了,select 你真的需要全部字段吗? 或许你说是! 再问你 你需要这个表将来添加的字段吗? 哦 这个就不晓得了 那麻烦你 把你需要的字段一 一 写出来 好不?

星号确实很便利,当不是这样用的,星号必须远离真实表

如下星号远离 真实表

select *

from

(

select f.*,rownum as rn

from

(

select  a.id,a.name,b.sex,b.btherday,c.country,c.address,d.email

from  a

inner  join b on a.id=b.id

   left  join c  on a.id=c.id

 right  join d on a.id=d.id

where 1=1

    and  a.name='shark'

order by a.name desc

) f

)

where rn <=10

第五优化大法 绑定变量法

此法只适应于 OLTP 和OLQP两种请求类型

所谓的绑定变量,也就是开发人员常见的参数,定义个形参,真实运行的时候传递个实参。如下 JAVA一般是问号

select  a.id,a.name,b.sex,b.btherday,c.country,c.address,d.email

from  a

inner  join b on a.id=b.id

   left  join c  on a.id=c.id

 right  join d on a.id=d.id

where 1=1

    and  a.name=?

为何要如此呢? 那是因为应用程序发给数据库的SQL 都是明文的字符编码,是我们人类看得懂的,机器看不懂的。机器只看得懂二进制的1010010。

因此数据库需要把传来的字符SQL命令翻译成二进制的1010,而这个工作叫做 编译或者是解释。搞过开发人都知道编译和解释都需要耗费CPU时间的。

虽然一个简单的SQL编译一次消耗不了多少CPU时间。如下图

<Oracle优化新常态> 第五章 SQL优化大法

其中4个SELECT 执千次以上,一天时间内24小时中。还好目前这些语句都绑定了变量。
<Oracle优化新常态> 第五章 SQL优化大法

虽然小凡仙我觉得,这五条大法或许很多人看不上眼,或许也解决不了你目前的SQL性能问题。如果你结合强拆,分库分表,三大配置,已经本章的5大法。那么小仙认为你的数据库性能问题,基本上进一步缩小在可控的范围中。

上一篇:HDU 5860 Death Sequence(递推)


下一篇:css3制作旋转动画