当有 ORDER BY 时 Oracle 不使用额外的索引

标签 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;

一个索引:on 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 上的第二个索引。

谢谢大家!

最佳答案

我举一个反例,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 行中的一行)。

关于当有 ORDER BY 时 Oracle 不使用额外的索引,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37783356/

相关文章:

java - 向 SQL 表添加用户定义的约束

oracle - 使用 Pentaho Kettle 将字符串转换为 bool 值

postgresql - 选择不同的。最适合减少等待时间的技术

当使用表时,jQuery 仅返回第一个元素的索引

python - 尝试访问列表元素时出现索引错误

python - 匹配并索引所有子字符串,包括重叠的子字符串

python - 如何获取列表的最后一个索引?

asp.net - 无法加载文件或程序集 'Oracle.DataAccess' 或其依赖项之一。试图加载格式不正确的程序

c# - Oracle Sequence nextval 是来回跳数

SQL 替换智能引号