【问题标题】:Query not using indexes -查询不使用索引 -
【发布时间】:2013-05-25 13:17:46
【问题描述】:

有什么方法可以在 REFERENCETYPE 列上使用索引。下面是表结构和执行计划。

SQL> desc messaginginbox
 Name                                      Null?    Type
 ----------------------------------------- -------- ---------------------------
 MESSAGINGINBOXID                          NOT NULL VARCHAR2(28)
 REFERENCEID                               NOT NULL VARCHAR2(28)
 REFERENCETYPE                                      VARCHAR2(1)
 LISTINGID                                 NOT NULL VARCHAR2(28)
 CREATEDATE                                         DATE
 LASTUPDATED                               NOT NULL DATE
 UPDATEDBY                                 NOT NULL VARCHAR2(28)
 RENTERLISTINGMANAGERID                             VARCHAR2(28)
 OWNERLISTINGMANAGERID                              VARCHAR2(28)
 OCA                                       NOT NULL NUMBER(38)

SQL> create index idx_MESSAGINGIN_REFERENCE on MESSAGINGINBOX( REFERENCETYPE);
Index created.

SQL> analyze table MESSAGINGINBOX compute statistics;

Table analyzed.

SQL> select * from MESSAGINGINBOX where referencetype='B';

55 rows selected.

Execution Plan
-------------------------------------------------------------------------
| Id  | Operation         | Name           | Rows  | Bytes | Cost (%CPU)|
-------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |                | 71354 |     9M|   873   (1)|
|*  1 |  TABLE ACCESS FULL| MESSAGINGINBOX | 71354 |     9M|   873   (1)|
-------------------------------------------------------------------------

SQL> create bitmap index idx_MESSAGINGIN_REFERENCE
     on MESSAGINGINBOX( REFERENCETYPE);

Index created.

SQL> analyze table MESSAGINGINBOX compute statistics;

Table analyzed.

SQL> select * from MESSAGINGINBOX where referencetype='B';

55 rows selected.

Execution Plan
-------------------------------------------------------------------------
| Id  | Operation         | Name           | Rows  | Bytes | Cost (%CPU)|
-------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |                | 71354 |     9M|   873   (1)|
|*  1 |  TABLE ACCESS FULL| MESSAGINGINBOX | 71354 |     9M|   873   (1)|
-------------------------------------------------------------------------

SQL> select count(*) from MESSAGINGINBOX;

  COUNT(*)
----------
    142707

SQL> select distinct referencetype from MESSAGINGINBOX;

REFERENCETYPE
-------------
I             
B       

SQL> select count(distinct referencetype) from MESSAGINGINBOX;

COUNT(DISTINCTREFERENCETYPE)
----------------------------
                   2

【问题讨论】:

  • 你能运行select referenceType, count(*) from MessageInbox group by referenceType;吗?目前 Oracle 假设一半的值是“B”,一半是“I”。如果这是准确的,那么您可能不想使用索引。如果这不准确,请先尝试将analyze table 替换为begin dbms_stats.gather_table_stats(<schema>, 'MESSAGINGINBOX'); end;。此外,如果将列 ReferenceType 设置为 NOT NULL,这将使 Oracle 在更多情况下使用索引。
  • 感谢 Jonearles,它对我有用,我使用 DBMS_STATS 视图来收集统计信息。位图索引在这里有效地工作。 I = 142652 和 B 的计数只有 55。我想知道是否有任何替代位图​​索引。问这个问题的原因是,我需要在我们使用不允许使用位图索引的标准版的不同机器上执行这个查询。

标签: database performance oracle sqlplus


【解决方案1】:

一个常规的 Btree 索引应该在这里工作,你只需要确保 ReferenceType 列上有一个histogram

列数据有偏差,优化器似乎知道这一点。它正确地将非重复计数估计为 2,这就是为什么行估计几乎完全是行数除以 2。在默认设置下,Oracle 根据数据分布和列的使用方式创建直方图。

将已弃用的analyze table 替换为dbms_stats.gather_table_stats,并确保使用所有默认选项。在收集统计信息之前,您还需要对 ReferenceType 运行查询过滤。默认情况下,如果列未在谓词中使用,Oracle 不会创建直方图。

运行此查询以查看是否生成了直方图:

select owner, table_name, column_name, histogram
from all_tab_columns
where table_name= 'MESSAGINGINBOX';

直方图至少应该改进估计的基数(行),这是修复执行计划的第一步。

【讨论】:

    【解决方案2】:

    一种方法是利用索引不包括完整列集为 NULL 的条目这一事实 - 即创建一个仅索引“B”记录的基于函数的索引:

    CREATE INDEX just_the_Bs ON messaginginbox
      (CASE WHEN referencetype='B' THEN 'B' END);
    

    但是,要使用索引,您必须使用相同的表达式进行查询,例如:

    SELECT * FROM messaginginbox
    WHERE (CASE WHEN referencetype='B' THEN 'B' END) = 'B';
    

    鉴于与该谓词匹配的数据量相对较小,我预计这很可能会使用索引范围扫描,然后按 rowid 进行表查找。

    另一种方法是使用提示 - 但请注意,您可能需要添加更多提示(例如 USE_NL)以避免一些非常糟糕的执行计划:

    SELECT /*+INDEX(messaginginbox,idx_MESSAGINGIN_REFERENCE)*/ *
    FROM messaginginbox WHERE referencetype='B';
    

    请特别注意此处关于提示的建议 - 可能需要很多提示才能强制 CBO 使用特定计划:http://jonathanlewis.wordpress.com/2013/05/28/how-to-hint/

    【讨论】:

      猜你喜欢
      • 2013-05-19
      • 1970-01-01
      • 2012-05-21
      • 2013-07-10
      • 2019-11-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多