ITPub博客

首页 > 数据库 > Oracle > [20191219]降序索引与取最大值.txt

[20191219]降序索引与取最大值.txt

原创 Oracle 作者:lfree 时间:2019-12-19 15:31:54 1 删除 编辑

[20191219]降序索引与取最大值.txt

--//开发滥用降序索引,今天发现一个问题就是取最大值.通过例子说明:

1.环境:
SCOTT@book> @ ver1
PORT_STRING         VERSION        BANNER
------------------- -------------- ----------------------------------------------------------------------------
x86_64/Linux 2.4.xx 11.2.0.4.0     Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production

SCOTT@book> alter system set pga_aggregate_target=4G;
System altered.

SCOTT@book> create table t (id1 number,id2 number,vc varchar2(50));
Table created.

create index i_t_id1 on t (id1);
create index i_t_id2desc on t (id2 desc);

SCOTT@book> insert into t select rownum,rownum,lpad('a',50,'a') from dual connect by level<=1e6;
1000000 rows created.

SCOTT@book> commit ;
Commit complete.

--//分析略.
SCOTT@book> select index_name,index_type,blevel,leaf_blocks,distinct_keys from dba_indexes where owner=user  and table_name='T';
INDEX_NAME                     INDEX_TYPE                      BLEVEL LEAF_BLOCKS DISTINCT_KEYS
------------------------------ --------------------------- ---------- ----------- -------------
I_T_ID1                        NORMAL                               2        1999       1000000
I_T_ID2DESC                    FUNCTION-BASED NORMAL                2        4283       1000000
--//I_T_ID2DESC的LEAF_BLOCKS=4283.

2.测试:
SCOTT@book> select max(id1) from t;
  MAX(ID1)
----------
   1000000

SCOTT@book> @ dpc '' ''
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  25ktx1ht4fs1u, child number 0
-------------------------------------
select max(id1) from t
Plan hash value: 2049239052
--------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                  | Name    | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |
--------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT           |         |      1 |        |       |     3 (100)|          |      1 |00:00:00.01 |       3 |
|   1 |  SORT AGGREGATE            |         |      1 |      1 |     5 |            |          |      1 |00:00:00.01 |       3 |
|   2 |   INDEX FULL SCAN (MIN/MAX)| I_T_ID1 |      1 |      1 |     5 |     3   (0)| 00:00:01 |      1 |00:00:00.01 |       3 |
--------------------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$1
   2 - SEL$1 / T@SEL$1
--//id1字段建立的是普通索引,取最大值仅仅3个逻辑读.

SCOTT@book> select max(id2) from t;
  MAX(ID2)
----------
   1000000

SCOTT@book> @ dpc '' ''
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  gtagtkz33v9n8, child number 0
-------------------------------------
select max(id2) from t
Plan hash value: 2966233522
---------------------------------------------------------------------------------------------------------------------
| Id  | Operation          | Name | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |
---------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |      1 |        |       |  2743 (100)|          |      1 |00:00:00.22 |    9285 |
|   1 |  SORT AGGREGATE    |      |      1 |      1 |     5 |            |          |      1 |00:00:00.22 |    9285 |
|   2 |   TABLE ACCESS FULL| T    |      1 |   1000K|  4882K|  2743   (1)| 00:00:33 |   1000K|00:00:00.13 |    9285 |
---------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$1
   2 - SEL$1 / T@SEL$1
--//执行计划走的是全表扫描.无法充分利用降序索引.

SCOTT@book> alter table t modify (id2 not null);
Table altered.

SCOTT@book> select  max(id2) from t;
  MAX(ID2)
----------
   1000000

SCOTT@book> @ dpc '' ''
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  6pj15dkuv35kb, child number 0
-------------------------------------
select  max(id2) from t
Plan hash value: 2206409122
-------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation             | Name        | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |             |      1 |        |       |  1166 (100)|          |      1 |00:00:00.25 |    4362 |
|   1 |  SORT AGGREGATE       |             |      1 |      1 |     5 |            |          |      1 |00:00:00.25 |    4362 |
|   2 |   INDEX FAST FULL SCAN| I_T_ID2DESC |      1 |   1000K|  4882K|  1166   (1)| 00:00:14 |   1000K|00:00:00.11 |    4362 |
-------------------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$1
   2 - SEL$1 / T@SEL$1
--//即使设置id2 not null,执行计划选择的也是INDEX FAST FULL SCAN,逻辑读依旧很高.
--//几乎很少有人这么写:
SCOTT@book> select id2 from (select  id2 from t order by id2 desc) where rownum=1;
       ID2
----------
   1000000

SCOTT@book> @ dpc '' ''
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  8aska3nqm81p2, child number 0
-------------------------------------
select id2 from (select  id2 from t order by id2 desc) where rownum=1
Plan hash value: 893305471
---------------------------------------------------------------------------------------------------------------------------
| Id  | Operation         | Name        | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |
---------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |             |      1 |        |       |     3 (100)|          |      1 |00:00:00.01 |       3 |
|*  1 |  COUNT STOPKEY    |             |      1 |        |       |            |          |      1 |00:00:00.01 |       3 |
|   2 |   VIEW            |             |      1 |      1 |    13 |     3   (0)| 00:00:01 |      1 |00:00:00.01 |       3 |
|   3 |    INDEX FULL SCAN| I_T_ID2DESC |      1 |   1000K|  4882K|     3   (0)| 00:00:01 |      1 |00:00:00.01 |       3 |
---------------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$1
   2 - SEL$2 / from$_subquery$_001@SEL$1
   3 - SEL$2 / T@SEL$2
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter(ROWNUM=1)

3.继续探究:
--//如果执行如下,看执行计划可以发现:
select  * from t where id2=1 ;

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access(SYS_OP_DESCEND("ID2")=HEXTORAW('3EFDFF') )
       filter(SYS_OP_UNDESCEND(SYS_OP_DESCEND("ID2"))=1)

--//但是如果你执行如下:
SCOTT@book> select SYS_OP_UNDESCEND(SYS_OP_DESCEND("ID2")),id2  from t where rownum=1;
SYS_OP_UNDESCEND(SYS_OP_DESCEND("ID2")) ID2
--------------------------------------- ---
C20539                                  456
--//利用这样的也可以获得对应编码.但是SYS_OP_UNDESCEND(SYS_OP_DESCEND("ID2"))不会等于id2.

SCOTT@book> select SYS_OP_UNDESCEND(SYS_OP_DESCEND(1)),1 from dual;
SYS_          1
---- ----------
C102          1

--//为什么oracle执行中filter可以(SYS_OP_UNDESCEND(SYS_OP_DESCEND("ID2"))=1)?有点搞不懂....
--//而实际上返回的是对应数字的oracle编码.

SCOTT@book> select utl_raw.cast_to_number(x) from (select (SYS_OP_UNDESCEND(min(SYS_OP_DESCEND(ID2)))) x from t);
UTL_RAW.CAST_TO_NUMBER(X)
-------------------------
                  1000000
--//注意是取最小值.开发更不可能这样写!!
SCOTT@book> @ dpc '' ''
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  3vk18a82yxt7y, child number 0
-------------------------------------
select utl_raw.cast_to_number(x) from (select
(SYS_OP_UNDESCEND(min(SYS_OP_DESCEND(ID2)))) x from t)
Plan hash value: 2062024120
-------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                   | Name        | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |             |      1 |        |       |     3 (100)|          |      1 |00:00:00.01 |       3 |
|   1 |  VIEW                       |             |      1 |      1 |    19 |     3   (0)| 00:00:01 |      1 |00:00:00.01 |       3 |
|   2 |   SORT AGGREGATE            |             |      1 |      1 |     6 |            |          |      1 |00:00:00.01 |       3 |
|   3 |    INDEX FULL SCAN (MIN/MAX)| I_T_ID2DESC |      1 |      1 |     6 |     3   (0)| 00:00:01 |      1 |00:00:00.01 |       3 |
-------------------------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$2 / from$_subquery$_001@SEL$1
   2 - SEL$2
   3 - SEL$2 / T@SEL$2

--//补充一点如果这些写:
SCOTT@book> select utl_raw.cast_to_number(SYS_OP_UNDESCEND(min(SYS_OP_DESCEND(ID2)))) from t;
UTL_RAW.CAST_TO_NUMBER(SYS_OP_UNDESCEND(MIN(SYS_OP_DESCEND(ID2))))
------------------------------------------------------------------
                                                           1000000

SCOTT@book> @ dpc '' ''
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  9bd7fdyvd2sh6, child number 0
-------------------------------------
select utl_raw.cast_to_number(SYS_OP_UNDESCEND(min(SYS_OP_DESCEND(ID2)))
) from t
Plan hash value: 2206409122
-------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation             | Name        | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |             |      1 |        |       |  1166 (100)|          |      1 |00:00:00.22 |    4362 |
|   1 |  SORT AGGREGATE       |             |      1 |      1 |     6 |            |          |      1 |00:00:00.22 |    4362 |
|   2 |   INDEX FAST FULL SCAN| I_T_ID2DESC |      1 |   1000K|  5859K|  1166   (1)| 00:00:14 |   1000K|00:00:00.12 |    4362 |
-------------------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$1
   2 - SEL$1 / T@SEL$1
21 rows selected.

--//这样写oracle无法充分利用取min/max的特性.不知道为什么,那位解析看看.

4.总结:
--//1.降序索引不能乱用.
--//2.降序索引适应的场景很少,仅仅oraer by a desc,b asc之类的一正一反可以使用.我仅仅能找到这个例子.
--//3.降序索引对于自增序列字段会导致索引变大的可能.
--//4.总之不要张冠李戴不加思索的乱用任何技术,再次看到一个豆腐渣中豆腐渣工程.

来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/267265/viewspace-2669477/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论
熟悉oracle相关技术,擅长sql优化,rman备份与恢复,熟悉linux shell编程。

注册时间:2008-01-03

  • 博文量
    4006
  • 访问量
    7969289