如果有ORDER BY,Oracle不会使用额外的索引

时间:2016-06-13 06:48:46

标签: oracle indexing

我正在玩Oracle 12和索引...

在这样的查询中:

SELECT a, b, c FROM table WHERE col1 = val1 AND col2 = val2 ORDER BY id DESC

(其中id是表的主键),Oracle总是使用主键上的索引。

因此,即使我在列col1和col2上创建索引,因为有ORDER BY语句,它也不会使用索引。

我可以推断这是一般规则吗?如果我的所有查询都包含" ORDER BY ID"我是否应该永远不会添加额外的索引? ?

这是我的表结构:

ID                  NUMBER GENERATED ALWAYS AS IDENTITY NOCACHE ORDER,
USERNAME            VARCHAR2(30 CHAR)   
TYPE_A              CHAR(1 BYTE)        
TYPE_B              CHAR(1 BYTE)        
CREATED             DATE        
UPDATED             DATE    

ALTER TABLE my_table
    ADD CONSTRAINT my_table_pk
    PRIMARY KEY (ID) 
    USING INDEX TABLESPACE XXX;

在表格中,我只执行此查询:

SELECT id, USERNAME, TYPE_A, TYPE_B, CREATED FROM table
where username = 'MYUSER' 
AND created >= TO_DATE('2016-01-01','YYYY-MM-DD')
AND created <= TO_DATE('2016-06-30','YYYY-MM-DD')
AND TYPE_A = 1
order by ID desc;

一个索引:在pk(ID)上(由oracle自动创建)

-------------------------------------------------------------------------------------------------------
| Id  | Operation                   | Name                    | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |                         |     2 |   384 |     1   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS BY INDEX ROWID| table                   |     2 |   384 |     1   (0)| 00:00:01 |
|   2 |   INDEX FULL SCAN DESCENDING| INDEX_PK                |    10 |       |     1   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------------

两个索引:首先是pk,第二个是(USERNAME,CREATED,TYPE_A)

-------------------------------------------------------------------------------------------------------
| Id  | Operation                   | Name                    | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |                         |     2 |   384 |     1   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS BY INDEX ROWID| table                   |     2 |   384 |     1   (0)| 00:00:01 |
|   2 |   INDEX FULL SCAN DESCENDING| INDEX_PK                |    10 |       |     1   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------------

所以第二个指数似乎毫无用处。

顺便说一句如果我删除ORDER BY语句,Oracle会使用USERNAME,CREATED,TYPE_A上的第二个索引。

全部谢谢!

2 个答案:

答案 0 :(得分:3)

嗯,简而言之 - 不,但我们不能给你一个通用规则,因为每次都会有很多不同的变量。对于更具体的答案,您应该包括此查询的解释计划,并且我们将更好地了解它不使用索引的原因。

只要首先指定ID列,Oracle就会知道使用此索引。

您不应该为选项添加不必要的索引,这些选择只会在很多时间内发生一次,或者那些缓慢但不会太慢的选择。您应该只添加与此表中最常见的选择/更新相关的索引。

如果col1col2上的过滤器选择重复,那么很可能(再次,我不知道您在此表上正在执行的其他进程)所有3列上的索引会更好:

(ID,Col1,Col2)

答案 1 :(得分:3)

让我给你一个反例,表明有些情况下Oracle会使用第二个索引。

SQL> create table tab (
  2  ID          NUMBER GENERATED ALWAYS AS IDENTITY NOCACHE ORDER,
  3  USERNAME        VARCHAR2(30 CHAR),
  4  TYPE_A      CHAR(1 BYTE),
  5  TYPE_B      CHAR(1 BYTE),
  6  CREATED         DATE,
  7  UPDATED         DATE
  8  )
  9  /

Table created.

SQL> alter table tab add constraint tab_pk primary key (id) using index
  2  /

Table altered.

SQL> create index SECOND_IDX on tab(username, created, type_a)
  2  /

Index created.

SQL> insert into tab(username, type_a, type_b, created)
  2  select 'OTHER_USER', '2', '2', date '2015-06-01'
  3  from all_objects, all_objects where rownum <= 1e5;

100000 rows created.

SQL> 
SQL> update tab
  2  set username = 'MYUSER',
  3      created = DATE '2016-06-01',
  4      type_a = '1'
  5   where id = 50000;

1 row updated.

SQL> commit;

Commit complete.

SQL> begin
  2     dbms_stats.gather_table_stats(ownname => USER,
  3        tabname => 'TAB',
  4        estimate_percent => 100,
  5        method_opt => 'FOR ALL INDEXED COLUMNS'
  6     );
  7  end;
  8  /

PL/SQL procedure successfully completed.

SQL> 
SQL> set autotrace traceonly exp
SQL> 
SQL> SELECT id, USERNAME, TYPE_A, TYPE_B, CREATED FROM tab
  2  where username = 'MYUSER'
  3  AND created >= TO_DATE('2016-01-01','YYYY-MM-DD')
  4  AND created <= TO_DATE('2016-06-30','YYYY-MM-DD')
  5  AND TYPE_A = '1'
  6  order by ID desc;

Execution Plan
----------------------------------------------------------
Plan hash value: 3658386757

---------------------------------------------------------------------------------------------------
| Id  | Operation                            | Name       | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                     |            |     1 |    29 |     5  (20)| 00:00:01 |
|   1 |  SORT ORDER BY                       |            |     1 |    29 |     5  (20)| 00:00:01 |
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED| TAB        |     1 |    29 |     4   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN                  | SECOND_IDX |     1 |       |     3   (0)| 00:00:01 |
---------------------------------------------------------------------------------------------------

在这种情况下,使用第二个索引的原因是极高的选择性(100000中的一行)。