【问题标题】:Need help in understanding a plan- Oracle在理解计划方面需要帮助 - Oracle
【发布时间】:2016-02-21 16:14:54
【问题描述】:

我有一张桌子

index_test(id number,empid number,name varchar2(30));

我在 empid 和 name 列上创建了一个复合索引。 我正在测试一个概念,当我使用 OR 子句时不使用索引。
所以我写了这个查询

select * from index_test where empid='950604' or name='5OMVXGH6G5';

但我得到了一个出乎我意料的结果。它确实使用索引,但也扫描整个表。请帮助我了解这里发生了什么。

这是计划-

> ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 2255565997                                                                                                                                                                                                                                                                                  

--------------------------------------------------------------------------------------------------------                                                                                                                                                                                                     
| Id  | Operation                    | Name                    | Rows  | Bytes | Cost (%CPU)| Time     |                                                                                                                                                                                                     
--------------------------------------------------------------------------------------------------------                                                                                                                                                                                                     
|   0 | SELECT STATEMENT             |                         |    11 |   231 |  9695   (1)| 00:01:57 |                                                                                                                                                                                                     
|   1 |  CONCATENATION               |                         |       |       |            |          |                                                                                                                                                                                                     
|*  2 |   TABLE ACCESS FULL          | INDEX_TEST              |     1 |    21 |  9682   (1)| 00:01:57 |                                                                                                                                                                                                     
|   3 |   TABLE ACCESS BY INDEX ROWID| INDEX_TEST              |    10 |   210 |    13   (0)| 00:00:01 |                                                                                                                                                                                                     
|*  4 |    INDEX RANGE SCAN          | SAMPLE_INDEX_INDEX_TEST |    10 |       |     3   (0)| 00:00:01 |                                                                                                                                                                                                     
--------------------------------------------------------------------------------------------------------                                                                                                                                                                                                     

Predicate Information (identified by operation id):                                                                                                                                                                                                                                                          
---------------------------------------------------                                                                                                                                                                                                                                                          

   2 - filter("NAME"='5OMVXGH6G5')                                                                                                                                                                                                                                                                           
   4 - access("EMPID"=950604)                                                                                                                                                                                                                                                                                
       filter(LNNVL("NAME"='5OMVXGH6G5'))   

【问题讨论】:

  • “直奔主题”是矛盾的说法。下次,请直接说出您的问题,而不是包括问候和这些无意义的短语:) 无论如何,我们是来帮忙的!
  • 另外,不要仅仅链接到在我们的浏览器中可能无法正常工作的内容。刚刚在您的问题中包含了一个详细的解释你期望得到什么以及你实际得到什么。
  • “我得到了一个意想不到的结果”,是的,但是您做了什么
  • @Asfakul Islam 请运行命令:explain plan for select * from ...... (rest your query) 然后SELECT * FROM table( DBMS_XPLAN.Display),然后复制最后一个查询的结果(请将其复制为文本!!!!- 不是 html,不是位图等等。请同时复制整个解释计划,包括打印在计划底部的“谓词信息”)并将其附加到问题中。您在此链接中显示的计划(作为 html)不完整且不可读。谢谢。
  • @kordirko 感谢 Kordiko。我已将计划包括在内。

标签: oracle plsql oracle11g


【解决方案1】:

如果您检查计划,您将看到这一行:| 1 | CONCATENATION
这是Oracle决定使用“OR扩展”优化方法的标志。

您可以在这里找到更多信息:https://docs.oracle.com/database/121/TGSQL/tgsql_transform.htm#GUID-0D5B9093-CDEA-45AC-A607-1F0D8F2615DD

简而言之:在这种方法中,Oracle 使用 OR 条件转换查询:

select * from index_test 
where empid='950604' or name='5OMVXGH6G5'

变成一种形式:

SELECT * FROM index_test 
   WHERE name='5OMVXGH6G5'
UNION ALL
SELECT * FROM TABLE
   WHERE empid='950604' and not( name='5OMVXGH6G5' )

为什么甲骨文这样做?好吧 - 我真的不知道。
这对我来说看起来很奇怪。
也许您的统计数据已经过时了?

【讨论】:

  • 统计数据不是陈旧的,在运行查询之前我运行了 dbms_stats.gather_table_stats 。
【解决方案2】:

因为您有一个 OR 条件,所以优化器需要获取两个查询的结果:一个与 empid 匹配,一个与名称匹配。

由于您选择 * - 所有列,因此优化器决定,除了仅使用索引 INDEX_TEST 之外,进行全扫描会更有效。

如果您能提及索引中有哪些列,我可以给出更有用的答案。

【讨论】:

  • 谢谢,name 和 empid 列上有索引,其中 name 是前导列。
  • 每个索引有哪些列?
  • 只有一个索引。 SAMPLE_INDEX_INDEX_TEST
  • INDEX_TEST 是表名。
  • 要了解发生了什么,您需要发布每个索引中涵盖了哪些列以及表上有哪些列(未索引)
【解决方案3】:

正如其他人已经提到的,查询中有一个OR 子句,这意味着oracle 必须独立查看OR 周围的两个条件。

查看解释计划,这是它的工作原理 -

步骤 1. 首先它会查看所有满足条件的行 -

NAME='5OMVXGH6G5'

Step 2. 然后它会寻找满足条件的行 -

EMPID=950604

(哦,请确保在将数字列与值进行比较时不要使用引号)

Step 3. 得到这两个结果后,将两者结合得到最终结果

您在(EMPID, NAME) 上有一个复合索引。这只会在您同时搜索empidname 的情况下对您有所帮助,而在单独搜索其中一个时则无济于事。正如预期的那样,它使用索引非常快地获得 EMPID(因为它是索引中的前导列),但除了 FULL TABLE SCAN 之外别无他法来搜索NAME

如果EMPID 是复合索引中的前导列,则您希望在搜索条件中包含empid。您搜索的第 1 步中只有 NAME,这就是它无法使用索引的原因。

你应该怎么做 -

  1. 如果复合索引未用于其他查询,而您刚刚为该查询创建了它,则将其删除。

  2. empidname 列上创建 2 个不同的索引。这样您的 step1 和 step2 都可以使用索引。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-01-11
    • 1970-01-01
    • 2011-02-17
    • 2012-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多