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

简介:   [20140210]一条sql语句的优化(11g).txt 今天下午看生产系统数据库,无意中发现一个错误,同时优化也有点小问题,写一个测试脚本。 1.建立测试环境: SCOTT@test> @ver BANNER --------------...

 

[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的索引。

目录
相关文章
|
16天前
|
SQL 存储 关系型数据库
一文搞懂SQL优化——如何高效添加数据
**SQL优化关键点:** 1. **批量插入**提高效率,一次性建议不超过500条。 2. **手动事务**减少开销,多条插入语句用一个事务。 3. **主键顺序插入**避免页分裂,提升性能。 4. **使用`LOAD DATA INFILE`**大批量导入快速。 5. **避免主键乱序**,减少不必要的磁盘操作。 6. **选择合适主键类型**,避免UUID或长主键导致的性能问题。 7. **避免主键修改**,保持索引稳定。 这些技巧能优化数据库操作,提升系统性能。
213 4
一文搞懂SQL优化——如何高效添加数据
|
1天前
|
SQL 关系型数据库 MySQL
【后端面经】【数据库与MySQL】为什么MySQL用B+树而不用B树?SQL优化:如何发现SQL中的问题?
【4月更文挑战第12天】数据库优化涉及硬件升级、操作系统调整、服务器/引擎优化和SQL优化。SQL优化目标是减少磁盘IO和内存/CPU消耗。`EXPLAIN`命令用于检查SQL执行计划,关注`type`、`possible_keys`、`key`、`rows`和`filtered`字段。设计索引时考虑外键、频繁出现在`where`、`order by`和关联查询中的列,以及区分度高的列。大数据表改结构需谨慎,可能需要停机、低峰期变更或新建表。面试中应准备SQL优化案例,如覆盖索引、优化`order by`、`count`和索引提示。优化分页查询时避免大偏移量,可利用上一批的最大ID进行限制。
11 3
|
18天前
|
SQL 关系型数据库 MySQL
【MySQL技术之旅】(7)总结和盘点优化方案系列之常用SQL的优化
【MySQL技术之旅】(7)总结和盘点优化方案系列之常用SQL的优化
36 1
|
19天前
|
SQL 索引
SQL怎么优化
SQL怎么优化
26 2
|
27天前
|
SQL 监控 测试技术
SQL语法优化与最佳实践
【2月更文挑战第28天】本章将深入探讨SQL语法优化的重要性以及具体的优化策略和最佳实践。通过掌握和理解这些优化技巧,读者将能够编写出更高效、更稳定的SQL查询,提升数据库性能,降低系统资源消耗。
|
1月前
|
SQL 关系型数据库 MySQL
[MySQL]SQL优化之sql语句优化
[MySQL]SQL优化之sql语句优化
|
1月前
|
SQL 关系型数据库 MySQL
[MySQL]SQL优化之索引的使用规则
[MySQL]SQL优化之索引的使用规则
|
5天前
|
SQL 人工智能 算法
【SQL server】玩转SQL server数据库:第二章 关系数据库
【SQL server】玩转SQL server数据库:第二章 关系数据库
40 10
|
1月前
|
SQL 数据库 数据安全/隐私保护
Sql Server数据库Sa密码如何修改
Sql Server数据库Sa密码如何修改
|
14天前
|
SQL
启动mysq异常The server quit without updating PID file [FAILED]sql/data/***.pi根本解决方案
启动mysq异常The server quit without updating PID file [FAILED]sql/data/***.pi根本解决方案
15 0