带有自定义函数的sql优化

SQL性能优化案例
本文分享了两个SQL查询的优化过程,通过添加提示和调整连接方式显著提高了查询效率,其中一个查询速度提升了1600倍,另一个提升了244倍。

author:skate
time:2009/05/14


 


一. 第一个sql
 
 Buffer Gets    Executions  Gets per Exec  %Total Time (s)  Time (s) Hash Value
--------------- ------------ -------------- ------ -------- --------- ----------
     60,414,728          195      309,819.1   28.1   619.83  11991.55  905212684

 

优化前:

 

SELECT
TOM.ORDER_ID
  FROM TB_ORDER_MATCH TOM
  LEFT JOIN TB_ORDER TOD ON TOD.ID = TOM.ORDER_ID
 WHERE TOD.WARE_ID = 32135
   AND TOM.GAME_NO = 245
   AND TOD.BUY_QUOMODO = 1
   AND TOD.STATE = 0
   AND TOD.IS_ADVANCED = 'Y'
 ORDER BY TOM.ORDER_ID

 

优化后:


 SELECT /*+ index(tod,PIDX_ORDER_WARE_ID)*/
TOM.ORDER_ID
  FROM TB_ORDER_MATCH TOM
  LEFT JOIN TB_ORDER TOD ON TOD.ID = TOM.ORDER_ID
 WHERE TOD.WARE_ID = 32135
   AND TOM.GAME_NO = 245
   AND TOD.BUY_QUOMODO = 1
   AND TOD.STATE = 0
   AND TOD.IS_ADVANCED = 'Y'
 ORDER BY TOM.ORDER_ID
          
这个sql特点是:执行的次数不是很少,每个sql执行非常耗逻辑读(309,819.1)  

 

优化前后对比:


优化前执行时间:1.6秒左右
优化后执行时间:0.001秒

 

请把以上这个sql放到相应位置          
          

          

2.第二,三个sql 

 

如下的优化过程已经优化的生产库

 

优化前后对比:

优化前执行时间:1.6秒左右

优化后执行时间:0.001

 

速度提高近1600


           CPU      Elapsd
  Buffer Gets    Executions  Gets per Exec  %Total Time (s)  Time (s) Hash Value
--------------- ------------ -------------- ------ -------- --------- ----------
     35,382,746           20    1,769,137.3   16.4   132.72    132.41  390687896

 

 SELECT
        w.ID AS ware_id,
        l.ID as lottery_id,
        l.lottery_name AS lottery_name,
        w.issue AS ware_issue,
        (f_recall_earliest_gametime(w.ID) + f_recall_advanced_by_gametime()) AS start_time
 FROM tb_lottery l,tb_ware w

 WHERE  l.ID = w.lottery_id AND l.sort_name <> 2 AND l.id <> 57 AND l.allow_advanced = 'Y'
 AND w.state IN (1,-1)
 AND NOT EXISTS
     (
       SELECT * FROM tb_recall_task rt
       WHERE rt.recall_type = 32 AND rt.ware_id = w.ID AND rt.game_no = 0 AND rt.is_done = 'Y'
     )
 AND (f_recall_earliest_gametime(w.ID) + f_recall_advanced_by_gametime()) <= to_date('2009-06-03 00:00:00','yyyy-mm-dd hh24:mi:ss')
ORDER BY  ware_id

 


这个语句的特点是虽然执行次数很少,仅有20次,但是他每次执行却消耗很多资源,读取buffer:1,769,137.3  其实超过100000的就说明这个sql的性能已经很差了

 

这也sql在优化前执行速度:7.83秒

 

状态:
TB_WARE: 14191
TB_LOTTERY; 58
TB_RECALL_TASK; 249276

 

这个sql返回很少的行,本sql返回8行记录


分析从这语句中执行计划中可以看到,执行计划很好,但就为什么还慢呢,我于是采用试探法

 

1.把语句简化为:

 

 SELECT
        w.ID AS ware_id,
        l.ID as lottery_id,
        l.lottery_name AS lottery_name,
        w.issue AS ware_issue,
        (f_recall_earliest_gametime(w.ID) + f_recall_advanced_by_gametime()) AS start_time
 FROM tb_lottery l,tb_ware w

 WHERE  l.ID = w.lottery_id AND l.sort_name <> 2 AND l.id <> 57 AND l.allow_advanced = 'Y'
 AND w.state IN (1,-1)
 /*AND NOT EXISTS
     (
       SELECT * FROM tb_recall_task rt
       WHERE rt.recall_type = 32 AND rt.ware_id = w.ID AND rt.game_no = 0 AND rt.is_done = 'Y'
     )*/
 AND (f_recall_earliest_gametime(w.ID) + f_recall_advanced_by_gametime()) <= to_date('2009-06-03 00:00:00','yyyy-mm-dd hh24:mi:ss')
ORDER BY  ware_id;

 

但sql的执行性能几乎没有变化,

 

2.猜想可能是因为那两个函数的原因

 

f_recall_earliest_gametime(w.ID)
f_recall_advanced_by_gametime()


sql进一步简化:

 

 SELECT
        w.ID AS ware_id,
        l.ID as lottery_id,
        l.lottery_name AS lottery_name,
        w.issue AS ware_issue,
       -- (f_recall_earliest_gametime(w.ID) + f_recall_advanced_by_gametime()) AS start_time
 FROM tb_lottery l,tb_ware w

 WHERE  l.ID = w.lottery_id AND l.sort_name <> 2 AND l.id <> 57 AND l.allow_advanced = 'Y'
 AND w.state IN (1,-1)
 /*AND NOT EXISTS
     (
       SELECT * FROM tb_recall_task rt
       WHERE rt.recall_type = 32 AND rt.ware_id = w.ID AND rt.game_no = 0 AND rt.is_done = 'Y'
     )*/
-- AND (f_recall_earliest_gametime(w.ID) + f_recall_advanced_by_gametime()) <= to_date('2009-06-03 00:00:00','yyyy-mm-dd hh24:mi:ss')
ORDER BY  ware_id;

 

这次在查询速度就发生了根本的变化,从7.83秒降低到 0.032秒,速度提高近244倍

 

尽然已经知道问题的原因,那就查看那两个函数

 

f_recall_earliest_gametime(w.ID)
f_recall_advanced_by_gametime()


以下两个函数内容都很简单,其中f_recall_advanced_by_gametime()就是返回常数值,这个实在是没办法优化了


f_recall_earliest_gametime(w.ID)中就一个语句,如下:

 

 

SELECT
MIN(G.GAME_TIME)
  FROM TB_GAME G
 INNER JOIN TB_MATCH M ON M.GAME_ID = G.ID
 WHERE M.WARE_ID = 20934

 

 观察这个语句的执行计划,发现这个sql占buffer :45331 ,对这个sql优化


tb_game  44640行
tb_match 146764行

 

 

这个sql,返回很少的记录行,但却占有那么多的逻辑读,一般情况是没走正确的索引或走全表扫描

 

 

SQL> SELECT
  2  MIN(G.GAME_TIME)
  3    FROM TB_GAME G
  4   INNER JOIN TB_MATCH M ON M.GAME_ID = G.ID
  5   WHERE M.WARE_ID = 20934;

1 row selected.

Elapsed: 00:00:00.84

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=18 Card=1 Bytes=22)
   1    0   SORT (AGGREGATE)
   2    1     NESTED LOOPS (Cost=18 Card=43 Bytes=946)
   3    2       TABLE ACCESS (FULL) OF 'TB_GAME' (Cost=18 Card=44640 B
          ytes=580320)

   4    2       INDEX (UNIQUE SCAN) OF 'AK_TB_MATCHA' (UNIQUE)

 


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
      45331  consistent gets
          0  physical reads
          0  redo size
        497  bytes sent via SQL*Net to client
        655  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

SQL>

 

调整之后的sql如下:

 

SELECT /*+ use_nl(g) index(g,PK_TB_GAME)*/
MIN(G.GAME_TIME)
  FROM TB_GAME G
 INNER JOIN TB_MATCH M ON M.GAME_ID = G.ID
 WHERE M.WARE_ID = 20934


 观察其执行计划如下:

 

 SQL> SELECT /*+ use_nl(g) index(g,PK_TB_GAME)*/
  2  MIN(G.GAME_TIME)
  3    FROM TB_GAME G
  4   INNER JOIN TB_MATCH M ON M.GAME_ID = G.ID
  5   WHERE M.WARE_ID = 20934;

1 row selected.

Elapsed: 00:00:00.60

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=45 Card=1 Bytes=22)
   1    0   SORT (AGGREGATE)
   2    1     NESTED LOOPS (Cost=45 Card=43 Bytes=946)
   3    2       INDEX (RANGE SCAN) OF 'AK_TB_MATCHA' (UNIQUE) (Cost=2
          Card=43 Bytes=387)

   4    2       TABLE ACCESS (BY INDEX ROWID) OF 'TB_GAME' (Cost=1 Car
          d=1 Bytes=13)

   5    4         INDEX (UNIQUE SCAN) OF 'PK_TB_GAME' (UNIQUE)

 


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          2  consistent gets
          0  physical reads
          0  redo size
        497  bytes sent via SQL*Net to client
        655  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

 

这个两个函数优化完了,再回到开始的那个sql,在执行时,发现其速度已经从7.83秒降到0.032秒

 


优化完毕,这两个sql就是我前两天抓取的,高逻辑读的前三个sql

 

 

 

 -----end---

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值