【问题标题】:How can I get a COUNT(col) ... GROUP BY to use an index?如何获得 COUNT(col) ... GROUP BY 以使用索引?
【发布时间】:2011-02-13 18:41:26
【问题描述】:

我有一个表 (col1, col2, ...),索引在 (col1, col2, ...) 上。该表中有数百万行,我想运行一个查询:

 SELECT col1, COUNT(col2) WHERE col1 NOT IN (<couple of exclusions>) GROUP BY col1

不幸的是,这会导致对表进行全表扫描,这需要一分钟以上的时间。有没有办法让 oracle 使用列上的索引来更快地返回结果?

编辑:

更具体地说,我正在运行以下查询:

SELECT owner, COUNT(object_name) FROM all_objects GROUP BY owner

并且在SYS.OBJ$ (SYS.I_OBJ2) 上有一个索引,它索引owner#name 列;我相信我应该能够在查询中使用这个索引,而不是对SYS.OBJ$进行全表扫描

【问题讨论】:

    标签: oracle group-by indexing query-optimization


    【解决方案1】:

    我有机会玩弄这个,而我之前关于 NOT IN 的 cmets 在这种情况下是一个红鲱鱼。关键是 NULL 的存在,或者更确切地说,索引列是否强制执行 NOT NULL 约束。

    这将取决于您使用的数据库版本,因为优化器会随着每个版本的发布而变得更加智能。我使用的是 11gR1,优化器在所有情况下都使用了索引,除了一个:当两列都为空并且我没有包含 NOT IN 子句时:

    SQL> desc big_table
     Name                                  Null?    Type
     -----------------------------------  ------    -------------------
     ID                                             NUMBER
     COL1                                           NUMBER
     COL2                                           VARCHAR2(30 CHAR)
     COL3                                           DATE
     COL4                                           NUMBER
    

    没有 NOT IN 子句...

    SQL> explain plan for
      2      select col4, count(col1) from big_table
      3      group by col4
      4  /
    
    Explained.
    
    SQL> select * from table(dbms_xplan.display)
      2  /
    
    PLAN_TABLE_OUTPUT
    ---------------------------------------------------------------------------------------
    Plan hash value: 1753714399
    
    ----------------------------------------------------------------------------------------
    | Id  | Operation          | Name      | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
    ----------------------------------------------------------------------------------------
    |   0 | SELECT STATEMENT   |           | 31964 |   280K|       |  7574   (2)| 00:01:31 |
    |   1 |  HASH GROUP BY     |           | 31964 |   280K|    45M|  7574   (2)| 00:01:31 |
    |   2 |   TABLE ACCESS FULL| BIG_TABLE |  2340K|    20M|       |  4284   (1)| 00:00:52 |
    ----------------------------------------------------------------------------------------
    
    9 rows selected.
    
    
    SQL>
    

    当我重新插入NOT IN 子句时,优化器选择使用索引。很奇怪。

    SQL> explain plan for
      2      select col4, count(col1) from big_table
      3      where col1 not in (12, 19)
      4      group by col4
      5  /
    
    Explained.
    
    SQL> select * from table(dbms_xplan.display)
      2  /
    
    PLAN_TABLE_OUTPUT
    ---------------------------------------------------------------------------------------
    Plan hash value: 343952376
    
    ----------------------------------------------------------------------------------------
    | Id  | Operation             | Name   | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
    ----------------------------------------------------------------------------------------
    |   0 | SELECT STATEMENT      |        | 31964 |   280K|       |  5057   (3)| 00:01:01 |
    |   1 |  HASH GROUP BY        |        | 31964 |   280K|    45M|  5057   (3)| 00:01:01 |
    |*  2 |   INDEX FAST FULL SCAN| BIG_I2 |  2340K|    20M|       |  1767   (2)| 00:00:22 |
    ----------------------------------------------------------------------------------------
    
    Predicate Information (identified by operation id):
    
    PLAN_TABLE_OUTPUT
    ----------------------------------------------------------------------------------------
    
       2 - filter("COL1"<>12 AND "COL1"<>19)
    
    14 rows selected.
    
    SQL>
    

    重复一遍,在所有其他情况下,只要索引列之一被声明为非空,则使用索引来满足查询。这在早期版本的 Oracle 中可能并非如此,但它可能指明了前进的方向。

    【讨论】:

    • 删除 NOT IN 仍然会导致对表进行全表扫描,
    【解决方案2】:

    您可以使用提示 http://download.oracle.com/docs/cd/B10501_01/server.920/a96533/hintsref.htm , 但请记住,使用索引可能并不总是会导致更快的执行。

    【讨论】:

      【解决方案3】:

      (以防万一,您确定它是在进行表扫描而不是索引扫描吗?)

      尝试使用COUNT(*) 而不是COUNT(col2)(当然,假设这适合您的问题)。此外,也许可以尝试仅使用 col1 的索引。

      【讨论】:

      • 查询计划中已获得 TABLE ACCESS FULL,成本为 2200。我无法在其上创建索引,但不应在 (col1, col2, ...) 上创建索引是否与 col1 上的索引相同,用于针对 col1 的查询?
      【解决方案4】:

      您正在查询 oracle 的固定表,因为您没有说明这是哪个数据库版本,我假设是最近的。是否已分析固定表并更新了统计信息?您是否通过使用 /*+ 规则 */ 提示尝试使用规则库优化器进行查询。我经常看到,在使用规则库优化器时,针对 oracle 自己的固定表的查询性能更好。

      【讨论】:

        猜你喜欢
        • 2016-04-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-09-30
        • 1970-01-01
        相关资源
        最近更新 更多