【问题标题】:Oracle 10g - optimize WHERE IS NOT NULLOracle 10g - 优化 WHERE IS NOT NULL
【发布时间】:2009-04-06 13:58:17
【问题描述】:

我们有 Oracle 10g,我们需要查询 1 个表(无连接)并过滤掉其中 1 个列为空的行。当我们这样做时——WHERE OurColumn IS NOT NULL——我们在一个非常大的表上进行全表扫描——坏坏坏。该列上有一个索引,但在这种情况下它会被忽略。有什么解决办法吗?

谢谢

【问题讨论】:

    标签: oracle optimization null


    【解决方案1】:

    优化器认为全表扫描会更好。

    如果只有几行NULL,则优化器是正确的。

    如果您绝对确定索引访问会更快(也就是说,您有超过75% 行和col1 IS NULL),那么提示您的查询:

    SELECT  /*+ INDEX (t index_name_on_col1) */
            *
    FROM    mytable t
    WHERE   col1 IS NOT NULL
    

    为什么是75%

    因为使用INDEX SCAN 来检索索引未涵盖的值意味着在ROWID 上进行隐藏连接,其成本约为4 的表扫描的倍数。

    如果索引范围包含的行数超过25%,则表扫描通常会更快。

    正如Tony Andrews 所提到的,聚类因子是衡量这个值的更准确的方法,但25% 仍然是一个很好的经验法则。

    【讨论】:

    • Quassnoi,你从哪里得到这 75%?如果有一百万行并且只有一个为空,为什么在这些列上使用索引会比表扫描慢?
    • 因为索引隐含了对 ROWID 的隐藏连接,其成本大约是表扫描的 4 倍。是不是索引选择性低于25%,表扫描通常比较快。
    • 在全表扫描中,您只需遍历表中的所有行;如果您进行索引扫描,您首先必须读取索引,然后读取表。从某个角度来看,读取索引的成本比读取整个表的成本要高。
    • 您需要注意这里的索引类型 - 如果它是单个列上的标准 B-Tree 索引,那么“is null”或“is not null”子句将永远不会使用索引。
    • IS NOT NULL 在可空列上使该列上的索引有资格包含在计划中,Oracle 对此足够聪明:)
    【解决方案2】:

    优化器将根据全表扫描的相对成本和使用索引来做出决定。这主要归结为必须读取多少块才能满足查询。另一个答案中提到的 25%/75% 的经验法则很简单:在某些情况下,即使获得 1% 的行,全表扫描也是有意义的——即,如果这些行恰好分布在许多块中。

    例如,考虑这个表:

    SQL> create table t1 as select object_id, object_name from all_objects;
    
    Table created.
    SQL> alter table t1 modify object_id null;
    
    Table altered.
    
    SQL> update t1 set object_id = null
      2  where mod(object_id,100) != 0
      3  /
    
    84558 rows updated.
    
    SQL> analyze table t1 compute statistics;
    
    Table analyzed.
    
    SQL> select count(*) from t1 where object_id is not null;
    
      COUNT(*)
    ----------
           861    
    

    如您所见,T1 中只有大约 1% 的行具有非空 object_id。但是由于我构建表格的方式,这 861 行将或多或少地均匀分布在表格周围。因此,查询:

    select * from t1 where object_id is not null;
    

    可能会访问 T1 中的几乎每个块来获取数据,即使优化器使用了索引。那么放弃索引并进行全表扫描是有意义的!

    帮助识别这种情况的一个关键统计数据是索引聚集因子:

    SQL> select clustering_factor from user_indexes where index_name='T1_IDX';
    
    CLUSTERING_FACTOR
    -----------------
                  460
    

    这个值 460 相当高(与索引中的 861 行相比),表明将使用全表扫描。见this DBAZine article on clustering factors

    【讨论】:

      【解决方案3】:

      如果您正在执行选择 *,那么执行表扫描而不是使用索引是有意义的。如果您知道您对哪些列感兴趣,您可以使用这些列加上您应用 IS NOT NULL 条件的列创建一个覆盖索引。

      【讨论】:

        【解决方案4】:

        这取决于您在表上的索引类型。

        大多数 B 树索引存储空条目。位图索引确实存储空条目。

        所以,如果你有:

        从我的表中选择 * 其中 mycolumn 为空

        并且您在mycolumn 上有一个标准的 B 树索引,那么查询不能使用该索引,因为“null”不在索引中。

        (如果索引针对多个列,并且索引列之一不为空,则索引中将有一个条目。)

        【讨论】:

        • 问题涉及is not null 查找,而不是is null
        【解决方案5】:

        在该列上创建索引。

        为了确保索引被使用,它应该在索引和其他列的where中。

        ocdecio 回答:

        如果您正在执行选择 *,那么执行表扫描而不是使用索引是有意义的。

        这并不完全正确;如果存在适合您的 where 子句的索引,则将使用索引,并且查询优化器决定使用该索引比执行表扫描更快。如果没有索引,或者没有合适的索引,则必须进行表扫描。

        【讨论】:

        • Select * 上的伟大 cmets 只是为了澄清一下 - 我们从不使用 SELECT * 其他原因 - 我们总是在 SELECT 子句中包含我们的列列表。
        【解决方案6】:

        还值得检查一下 Oracle 对表的统计信息是否是最新的。它可能不知道全表扫描会更慢。

        【讨论】:

          【解决方案7】:

          Oracle 数据库根本不会在常规(b-tree)索引中索引空值,因此它不能使用它,也不能强制 oracle 数据库使用它。

          BR

          【讨论】:

          • 这个问题是关于is NOT null的。所有相关值都将在索引中。
          【解决方案8】:

          使用提示只能作为一种变通方法,而不是一种解决方案。

          正如其他答案中提到的,空值在 B-TREE 索引中不可用。

          既然您知道在此列中大部分为空值,您是否可以将空值替换为例如范围。

          这实际上取决于您的列和数据的性质,但通常情况下,如果您的列是日期类型,例如:

          where mydatecolumn is not null 可以翻译成一条规则:我想要所有有日期的行。

          那么您绝对可以这样做: 其中 mydatecolumn

          这将返回所有带有日期的行并省略空值,同时利用该列上的索引而不使用任何提示。

          【讨论】:

            【解决方案9】:

            http://www.oracloid.com/2006/05/using-index-for-is-null/

            如果您的索引位于单个字段上,则不会使用它。尝试在索引中添加虚拟字段或常量:

            create index tind on t(field_to_index, 1); 
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2014-12-08
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2010-10-23
              • 2011-11-17
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多