[20140210]一条sql语句的优化(11g).txt

 

[20140210]一条sql语句的优化(11g).txt

今天下午看生产系统数据库,无意中发现一个错误,同时优化也有点小问题,写一个测试脚本。

1.建立测试环境:
SCOTT@test> @ver

BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production

create table t pctfree 99 as
select rownum id ,mod(rownum,200) idx,trunc(sysdate)-dbms_random.value(0,200) cr_date ,rpad(rownum,1000,'x') vc from dual connect by level

create unique index pk_t on t (id);
create index i_t_idx on t(idx);
create index i_t_cr_date on t(cr_date);

exec dbms_stats.gather_table_stats(null,'T',estimate_percent=>100,no_invalidate=>false,cascade=>true) ;

2.有问题语句:

SCOTT@test> column vc noprint
SCOTT@test> select * from t where cr_date = ( select max(cr_date) from t where idx=42);
        ID        IDX CR_DATE
---------- ---------- -------------------
      7842         42 2014-02-06 09:57:35

--很明显这样查询结果目前是正确的,但是实际上如果我修改如下:
update t set cr_date='2014-02-06 09:57:35' where id=1e4;
commit ;
--注意我这里取了一个巧,直接使用cr_date='2014-02-06 09:57:35',只要nls*相关参数正确,一般没有问题。
--再次执行:
SCOTT@test> select * from t where cr_date = ( select max(cr_date) from t where idx=42);
        ID        IDX CR_DATE
---------- ---------- -------------------
      7842         42 2014-02-06 09:57:35
     10000          0 2014-02-06 09:57:35

--很明显,开发需要是IDX=42的记录,而不需要第2条。只不过日期相重的概率很少罢了。BTW,我已经不止一次发现这样类似的错误。
--至少这样写才正确:

SCOTT@test> alter session set statistics_level=all;
SCOTT@test> select * from t where cr_date = ( select max(cr_date) from t where idx=42) and idx=42;
        ID        IDX CR_DATE
---------- ---------- -------------------
      7842         42 2014-02-06 09:57:35

SCOTT@test> @dpc '' ''
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  77hz50d71h2s9, child number 0
-------------------------------------
select * from t where cr_date = ( select max(cr_date) from t where
idx=42) and idx=42
Plan hash value: 2661465193
-------------------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                        | Name             | Starts | E-Rows | Cost (%CPU)| A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |
-------------------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                 |                  |      1 |        |    32 (100)|      1 |00:00:00.06 |      39 |       |       |          |
|   1 |  TABLE ACCESS BY INDEX ROWID     | T                |      1 |      1 |     2   (0)|      1 |00:00:00.06 |      39 |       |       |          |
|   2 |   BITMAP CONVERSION TO ROWIDS    |                  |      1 |        |            |      1 |00:00:00.06 |      38 |       |       |          |
|   3 |    BITMAP AND                    |                  |      1 |        |            |      1 |00:00:00.06 |      38 |       |       |          |
|   4 |     BITMAP CONVERSION FROM ROWIDS|                  |      1 |        |            |      1 |00:00:00.06 |      36 |       |       |          |
|*  5 |      INDEX RANGE SCAN            | I_T_CR_DATE      |      1 |      1 |     1   (0)|      2 |00:00:00.04 |      36 |       |       |          |
|   6 |       SORT AGGREGATE             |                  |      1 |      1 |            |      1 |00:00:00.04 |      34 |       |       |          |
|*  7 |        VIEW                      | index$_join$_002 |      1 |     50 |    30   (4)|     50 |00:00:00.04 |      34 |       |       |          |
|*  8 |         HASH JOIN                |                  |      1 |        |            |     50 |00:00:00.04 |      34 |  1096K|  1096K| 1580K (0)|
|*  9 |          INDEX RANGE SCAN        | I_T_IDX          |      1 |     50 |     1   (0)|     50 |00:00:00.01 |       2 |       |       |          |
|  10 |          INDEX FAST FULL SCAN    | I_T_CR_DATE      |      1 |     50 |    35   (0)|  10000 |00:00:00.01 |      32 |       |       |          |
|  11 |     BITMAP CONVERSION FROM ROWIDS|                  |      1 |        |            |      1 |00:00:00.01 |       2 |       |       |          |
|* 12 |      INDEX RANGE SCAN            | I_T_IDX          |      1 |      1 |     1   (0)|     50 |00:00:00.01 |       2 |       |       |          |
-------------------------------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   5 - access("CR_DATE"=)
   7 - filter("IDX"=42)
   8 - access(ROWID=ROWID)
   9 - access("IDX"=42)
  12 - access("IDX"=42)

--这样写,执行计划看上去很复杂。逻辑读39,实际上我的索引很小,生产系统不会选择INDEX FAST FULL SCAN I_T_CR_DATE 。

--生产系统实际上是这样
SCOTT@test> select /*+ index(t i_t_idx) */ * from t where cr_date = ( select /*+ index(t i_t_idx) */ max(cr_date) from t where idx=42) and idx=42;
        ID        IDX CR_DATE
---------- ---------- -------------------
      7842         42 2014-02-06 09:57:35

SCOTT@test> @dpc '' ''
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  60p18ha8myc2j, child number 0
-------------------------------------
select /*+ index(t i_t_idx) */ * from t where cr_date = ( select /*+
index(t i_t_idx) */ max(cr_date) from t where idx=42) and idx=42

Plan hash value: 1551695814

-------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                     | Name    | Starts | E-Rows | Cost (%CPU)| A-Rows |   A-Time   | Buffers | Reads  |
-------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |         |      1 |        |   102 (100)|      1 |00:00:00.01 |     105 |     59 |
|*  1 |  TABLE ACCESS BY INDEX ROWID  | T       |      1 |      1 |    51   (0)|      1 |00:00:00.01 |     105 |     59 |
|*  2 |   INDEX RANGE SCAN            | I_T_IDX |      1 |     50 |     1   (0)|     50 |00:00:00.01 |       3 |      0 |
|   3 |   SORT AGGREGATE              |         |      1 |      1 |            |      1 |00:00:00.01 |      52 |     58 |
|   4 |    TABLE ACCESS BY INDEX ROWID| T       |      1 |     50 |    51   (0)|     50 |00:00:00.01 |      52 |     58 |
|*  5 |     INDEX RANGE SCAN          | I_T_IDX |      1 |     50 |     1   (0)|     50 |00:00:00.01 |       2 |      0 |
-------------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("CR_DATE"=)
   2 - access("IDX"=42)
   5 - access("IDX"=42)
--很明显这样逻辑读有点高,到达105.主要的问题要使用索引范围扫描2次(I_T_IDX).

==更正如下:实际这样执行:
SCOTT@test> select /*+ index(t i_t_cr_date) */ * from t where cr_date = ( select /*+ index(t i_t_idx) */ max(cr_date) from t where idx=42) and idx=42;
SCOTT@test> @dpc '' ''
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  a8kx1xu0c7cmh, child number 1
-------------------------------------
select /*+ index(t i_t_cr_date) */ * from t where cr_date = ( select
/*+ index(t i_t_idx) */ max(cr_date) from t where idx=42) and idx=42
Plan hash value: 2838288168
------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                      | Name        | Starts | E-Rows | Cost (%CPU)| A-Rows |   A-Time   | Buffers | Reads  |
------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |             |      1 |        |    54 (100)|      1 |00:00:00.01 |      57 |    181 |
|*  1 |  TABLE ACCESS BY INDEX ROWID   | T           |      1 |      1 |     3   (0)|      1 |00:00:00.01 |      57 |    181 |
|*  2 |   INDEX RANGE SCAN             | I_T_CR_DATE |      1 |      1 |     1   (0)|      2 |00:00:00.01 |      55 |    180 |
|   3 |    SORT AGGREGATE              |             |      1 |      1 |            |      1 |00:00:00.01 |      52 |    164 |
|   4 |     TABLE ACCESS BY INDEX ROWID| T           |      1 |     50 |    51   (0)|     50 |00:00:00.01 |      52 |    164 |
|*  5 |      INDEX RANGE SCAN          | I_T_IDX     |      1 |     50 |     1   (0)|     50 |00:00:00.01 |       2 |     16 |
------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter("IDX"=42)
   2 - access("CR_DATE"=)
   5 - access("IDX"=42)
===========================

select * from t where (idx,cr_date ) in ( select idx, max(cr_date) from t where idx=42 group by idx ) ;
--也不是很好。
select * from (select * from t where idx=42 order by cr_date desc) where rownum

--建立索引
create index i_t_idx_cr_date on t(idx,cr_date);

SCOTT@test> select * from t where cr_date = ( select max(cr_date) from t where idx=42) and idx=42;
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  77hz50d71h2s9, child number 0
-------------------------------------
select * from t where cr_date = ( select max(cr_date) from t where
idx=42) and idx=42
Plan hash value: 3430031104
----------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                      | Name            | Starts | E-Rows | Cost (%CPU)| A-Rows |   A-Time   | Buffers | Reads  |
----------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |                 |      1 |        |     4 (100)|      1 |00:00:00.01 |       6 |      1 |
|   1 |  TABLE ACCESS BY INDEX ROWID   | T               |      1 |      1 |     2   (0)|      1 |00:00:00.01 |       6 |      1 |
|*  2 |   INDEX RANGE SCAN             | I_T_IDX_CR_DATE |      1 |      1 |     1   (0)|      1 |00:00:00.01 |       5 |      1 |
|   3 |    SORT AGGREGATE              |                 |      1 |      1 |            |      1 |00:00:00.01 |       2 |      1 |
|   4 |     FIRST ROW                  |                 |      1 |      1 |     2   (0)|      1 |00:00:00.01 |       2 |      1 |
|*  5 |      INDEX RANGE SCAN (MIN/MAX)| I_T_IDX_CR_DATE |      1 |      1 |     2   (0)|      1 |00:00:00.01 |       2 |      1 |
----------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("IDX"=42 AND "CR_DATE"=)
   5 - access("IDX"=42)

SCOTT@test> select * from (select * from t where idx=42 order by cr_date desc) where rownumSCOTT@test> @dpc '' ''
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  19pf4scday9pv, child number 0
-------------------------------------
select * from (select * from t where idx=42 order by cr_date desc)
where rownumPlan hash value: 2332835607
-------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                      | Name            | Starts | E-Rows | Cost (%CPU)| A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |                 |      1 |        |     4 (100)|      1 |00:00:00.01 |       3 |
|*  1 |  COUNT STOPKEY                 |                 |      1 |        |            |      1 |00:00:00.01 |       3 |
|   2 |   VIEW                         |                 |      1 |      2 |     4   (0)|      1 |00:00:00.01 |       3 |
|   3 |    TABLE ACCESS BY INDEX ROWID | T               |      1 |     50 |     4   (0)|      1 |00:00:00.01 |       3 |
|*  4 |     INDEX RANGE SCAN DESCENDING| I_T_IDX_CR_DATE |      1 |      2 |     2   (0)|      1 |00:00:00.01 |       2 |
-------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter(ROWNUM   4 - access("IDX"=42)

--这样写最好,当然前提是不会返回多行,好像实际不会出现这种情况。
--这样逻辑读确实下降不少,但是不是我需要,因为建立的索引有些冗余了。
drop index i_t_idx_cr_date ;

SCOTT@test> select * from (select * from t where idx=42 order by cr_date desc) where rownumSCOTT@test> @dpc '' ''
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  19pf4scday9pv, child number 0
-------------------------------------
select * from (select * from t where idx=42 order by cr_date desc)
where rownumPlan hash value: 587900075
--------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                      | Name    | Starts | E-Rows | Cost (%CPU)| A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |
--------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |         |      1 |        |    52 (100)|      1 |00:00:00.02 |      52 |       |       |          |
|*  1 |  COUNT STOPKEY                 |         |      1 |        |            |      1 |00:00:00.02 |      52 |       |       |          |
|   2 |   VIEW                         |         |      1 |     50 |    52   (2)|      1 |00:00:00.02 |      52 |       |       |          |
|*  3 |    SORT ORDER BY STOPKEY       |         |      1 |     50 |    52   (2)|      1 |00:00:00.02 |      52 | 73728 | 73728 |          |
|   4 |     TABLE ACCESS BY INDEX ROWID| T       |      1 |     50 |    51   (0)|     50 |00:00:00.02 |      52 |       |       |          |
|*  5 |      INDEX RANGE SCAN          | I_T_IDX |      1 |     50 |     1   (0)|     50 |00:00:00.01 |       2 |       |       |          |
--------------------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter(ROWNUM   3 - filter(ROWNUM   5 - access("IDX"=42)

--这样仅仅扫描i_t_idx一次。比原来逻辑读少一半。

总结:
1.修改语句如下:
select * from (select * from t where idx=42 order by cr_date desc) where rownum2.比较好的解决方式是建立idx,cr_date的复合索引。删除idx的索引。

时间: 2024-09-07 23:11:18

[20140210]一条sql语句的优化(11g).txt的相关文章

[20151209]一条sql语句的优化(续).txt

[20151209]一条sql语句的优化(续).txt http://blog.itpub.net/267265/viewspace-1852195/ --上次提到其中1条sql语句: 1.环境: SYSTEM@192.168.99.105:1521/dbcn> @ver1 PORT_STRING                    VERSION        BANNER ------------------------------ -------------- -------------

[20130319]一条sql语句的优化.txt

[20130319]一条sql语句的优化.txt 生产系统,遇到这样一条语句:SELECT MAX (LENGTH (pe_id)) FROM pe_master_index WHERE SUBSTR (pe_id, 1, 2) = 'TJ'; --真不知道开发人员如何想的,写出这样的语句.字段pe_id是主键.--数据库版本 SQL> select * from v$version where rownum BANNER                                     

[20131025]一条sql语句的优化.txt

[20131025]一条sql语句的优化.txt 最近在优化一条sql语句,做一个测试例子测试看看.遇到一些问题记录一下: 1.建立环境: SCOTT@test> @ver BANNER -------------------------------------------------------------------------------- Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Productio

sql server-求助:sqlserver一条sql语句的优化 是否需要索引 建立什么样的索引合适

问题描述 求助:sqlserver一条sql语句的优化 是否需要索引 建立什么样的索引合适 1C select COUNT(*) total from (select distinct(device_token) from MDM_POLICY_UPDATE where len(device_token)=64 and SW='crmi_poly') a 解决方案 我觉得直接可以用count(device_token) 然后直接groupby device个人感觉最拖后腿的应该是len()这个函

请教大神一条sql语句的优化

问题描述 请教大神一条sql语句的优化 表名pm,以temp分组,count两个信息,一个是全部个数,另一个是status为1 id temp status 1 1 0 2 1 1 3 2 0 4 2 1 5 2 1 select total.temp ,used.c1,total.c2 from (select temp,count(1) c1 from pm group by temp ) total left join (select temp,count(1) c2 from pm wh

[20150715]一条sql语句的优化.txt

[20150715]一条sql语句的优化.txt --生产系统发现一条语句. update presc_check t set t.diagnosis=replace(t.diagnosis,',慢性病drugs','') --第1眼看到的感觉真的很想骂人,什么能没有where条件呢? --我把这个表拷贝过来.这个表占用1G多1点,在测试环境执行看看: -- copy from system/xxxx@ip:1521/tyt create presc_check using select * f

[20120104]稳定一条sql语句的执行计划.txt

[20120104]稳定一条sql语句的执行计划.txt http://www.itpub.net/thread-1495845-1-1.htmlhttp://space.itpub.net/267265/viewspace-723066 ORACLE8I升级11G R2后,查询系统视图特别慢 我的测试版本:SQL> select * from v$version where rownumBANNER------------------------------------------------

分享几条sql语句命令优化技巧

1.应用程序中,保证在实现功能的基础上,尽量减少对数据库的访问次数:通过 搜索参数,尽量减少对表的访问行数,最小化结果集,从而减轻网络负担:能够分 开的操作尽量分开处理,提高每次的响应速度:在数据窗口使用SQL时,尽量把使 用的索引放在选择的首列:算法的结构尽量简单:在查询时,不要过多地使用通配 符如SELECT * FROM T1语句,要用到几列就选择几列如:SELECT COL1,COL2 FROM T1:在可能的情况下尽量限制尽量结果集行数如:SELECT TOP 300 COL1,COL

[20120319]一条sql语句的优化.txt

前天检查数据库,发现一天sql语句执行如下:SELECT MAX (undrug_code)   FROM undrug_info  WHERE SUBSTR (undrug_code, 1, 1) = 'F'; undrug_code是表undrug_info的主键.开始看见这个语句,感觉这样写不好,我想像的执行计划,全扫描索引,然后sort aggregate,找到最大值. 不如这样写:SELECT MAX (undrug_code)   FROM undrug_info  WHERE un