【问题标题】:Oracle not using indexes if queried in wrong order如果查询顺序错误,Oracle 不使用索引
【发布时间】:2021-10-23 13:54:17
【问题描述】:

我试图找到类似的问题,但没有找到。如果这是重复的,请指出正确的主题。

Oracle 版本 19 标准版 2,版本 19.3。

我有一张桌子正在预订。 部分专栏:

COLUMN_NAME DATA_TYPE NULLABLE
ID NUMBER(38,0) No
CLASSID NUMBER(5,0) Yes
PARENTFOREIGNKEY NUMBER(38,0) Yes
BOOKINGNO NUMBER(4,0) Yes

它有以下索引(对这个问题很重要):

INDEX_NAME UNIQUENESS STATUS INDEX_TYPE COLUMNS
BOOKING UNIQUE VALID NORMAL ID
BOOKING_511_2 NONUNIQUE VALID NORMAL PARENTFOREIGNKEY, CLASSID, ID

执行查询 SELECT * FROM BOOKING WHERE (CLASSID=511) AND PARENTFOREIGNKEY=31647961 ORDER BY BOOKINGNO; 大约需要 40-45 秒。 然而 SELECT * FROM BOOKING WHERE PARENTFOREIGNKEY=31647961 AND (CLASSID=511) ORDER BY BOOKINGNO; 只需要大约 0.013 秒。

很明显,第一个查询没有使用索引,而第二个查询是。说明计划是相同的。

|   0 | SELECT STATEMENT                     |                           |     4 |   648 |     9  (12)| 00:00:01 |
|   1 |  SORT ORDER BY                       |                           |     4 |   648 |     9  (12)| 00:00:01 |
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED| BOOKING     |     4 |   648 |     8   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN                  | BOOKING_511_2 |     4 |       |     4   (0)| 00:00:01 |
------------------------------------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   3 - access("PARENTFOREIGNKEY"=31647961 AND "CLASSID"=511)

谁能告诉我这是怎么回事?为什么两个查询在执行中不等效?

谢谢你, GG

编辑: 解释计划 (SELECT * FROM TABLE(dbms_xplan.display_cursor(format => 'BASIC +PREDICATE'));):

EXPLAINED SQL STATEMENT:
------------------------
SELECT * FROM BOOKING WHERE (CLASSID=:"SYS_B_0") AND 
PARENTFOREIGNKEY=:"SYS_B_1" ORDER BY BOOKINGNO
 
Plan hash value: 1769702959
 
----------------------------------------------------
| Id  | Operation          | Name                  |
----------------------------------------------------
|   0 | SELECT STATEMENT   |                       |
|   1 |  SORT ORDER BY     |                       |
|*  2 |   TABLE ACCESS FULL| BOOKING               |
----------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   2 - filter(("PARENTFOREIGNKEY"=:SYS_B_1 AND "CLASSID"=:SYS_B_0))

“快速”查询:

EXPLAINED SQL STATEMENT:
------------------------
SELECT * FROM BOOKING WHERE PARENTFOREIGNKEY=:"SYS_B_0" 
AND (CLASSID=:"SYS_B_1")  ORDER BY BOOKINGNO
 
Plan hash value: 3357352015
 
--------------------------------------------------------------------------
| Id  | Operation                            | Name                      |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT                     |                           |
|   1 |  SORT ORDER BY                       |                           |
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED| BOOKING                   |
|*  3 |    INDEX RANGE SCAN                  | BOOKING_511_2             |
--------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   3 - access("PARENTFOREIGNKEY"=:SYS_B_0 AND "CLASSID"=:SYS_B_1)

【问题讨论】:

  • 请提供两个实际执行计划,并在语句执行后立即执行dbms_xplan.display_cursor(format =&gt; 'BASIC +PREDICATE)。我无法在 db<>fiddle 中重现您的行为
  • 所以第一个查询跑得慢,第二个跑得快。如果您再次运行第一个查询,它会再次变慢吗?
  • @pmdba:是的,行为是一致的。所以,反应慢又慢,快又快(又快又慢……所有的组合都试过了,重复了)
  • @GoranGerlič 再次生成执行计划,但这次使用SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);。 “BASIC”格式不会显示“Note”部分,这对于调试计划问题可能很关键。通常,谓词顺序无关紧要。最常见的例外情况是,如果 DBA 创建了仅适用于一个特定 SQL_ID 的 SQL 配置文件,将在“注意”部分进行说明。

标签: oracle indexing


【解决方案1】:

搜索谓词中的列顺序需要与索引中的列顺序相匹配。来自“How to Create and Use Indexes in Oracle Database”:

Oracle 数据库从最左边(“第一个”)开始读取索引 柱子。然后它进入第二个,第三个等等。所以如果你有一个 索引:

create index i on tab ( col1, col2, col3 ); 

你的 where 子句是:

where col3 = 'value' 

要使用索引,数据库要么必须 遍历 col1 和 col2 中的所有值。或者,更有可能的是,阅读 整个过程都是为了找到符合您条件的那些。

在您的情况下,问题出在索引中的第三列。如果索引仅在 classidparentforeignkey 列上,则谓词的顺序不会有任何区别。通过将id 列添加到索引以及谓词顺序的更改,优化器确定只扫描整个表一次比使用索引更容易,因为它必须在其索引中扫描两次整体。文章中有这样的例子,在这里复制很长。

【讨论】:

  • 报价虽然准确,但并不完全等同于 OP 正在做什么。 OP 正在使用col1col2 进行查询,只是不是按照这个顺序。我不是说你的错,只是我不认为引用证明你是正确的。我有点惊讶 CBO 没有解决这个问题......
  • @pmdba:是的,我们发现了。有趣的是,虽然某些 Oracle 用户会发生这种情况,但某些用户却并非如此。这是同一台 Oracle 服务器。而且我们多年来没有观察到这种行为。它仅在本月开始。我们确实更改了查询现在匹配索引顺序的代码生成。
  • @pmdba 发现的另一件事:Oracle 如何使用此索引取决于 Oracle 安装。我们有几台服务器,每台服务器都有几个用户(很多客户)。如果查询的顺序不正确,一台服务器“决定”不使用索引。即使查询的顺序不同,其他服务器也会使用索引。造成这种差异的原因仍然未知。
  • @Ben 由于索引中的第 3 列,优化器出现问题。在该确切场景的文章中有一个示例。我认为复制整个内容太长了,这就是我在回答中提到它的原因。如果不是索引中的第 3 列,则搜索谓词中列的顺序无关紧要。第三列在谓词中未使用,如果谓词顺序关闭,则使用索引的成本更高。
  • @pmdba - 索引创建 DDL 或 ORDER BY 子句中的顺序可能很重要,但查询中谓词的顺序几乎无关紧要。这里肯定发生了一些奇怪的事情,比如有一个仅适用于特定 SQL_ID 的计划管理功能。 @GoranGerlič - 您不必更改代码生成以匹配索引顺序。相反,请在完整的执行计划以及 DBA_SQL_PROFILES、DBA_OUTLINES 和 DBA_REWRITE_EQUIVALENCES 等数据字典视图中查找环境差异。
猜你喜欢
  • 2016-05-03
  • 2018-03-14
  • 2017-10-05
  • 1970-01-01
  • 2014-05-30
  • 2013-02-19
  • 1970-01-01
  • 2015-09-08
  • 2016-11-26
相关资源
最近更新 更多