【问题标题】:Oracle 11g: Index not used in "select distinct"-queryOracle 11g:“选择不同”查询中未使用索引
【发布时间】:2011-11-24 06:20:18
【问题描述】:

我的问题是关于 Oracle 11g 和 SQL 查询中索引的使用。

在我的数据库中,有一个结构如下的表:

Table tab (
  rowid NUMBER(11),
  unique_id_string VARCHAR2(2000),
  year NUMBER(4),
  dynamic_col_1 NUMBER(11),
  dynamic_col_1_text NVARCHAR2(2000)
 ) TABLESPACE tabspace_data;

我创建了两个索引:

CREATE INDEX Index_dyn_col1 ON tab (dynamic_col_1, dynamic_col_1_text) TABLESPACE tabspace_index;
CREATE INDEX Index_unique_id_year ON tab (unique_id_string, year) TABLESPACE tabspace_index;

该表包含大约 1 到 2 百万条记录。我通过执行以下 SQL 命令从中提取数据:

SELECT distinct
 "sub_select"."dynamic_col_1" "AS_dynamic_col_1","sub_select"."dynamic_col_1_text" "AS_dynamic_col_1_text"
FROM 
(
    SELECT "tab".*  FROM "tab"
    where "tab".year = 2011
) "sub_select"

不幸的是,尽管我创建了上述两个索引,但查询需要大约 1 小时才能执行。 解释计划显示 Oracle 使用“表完全访问”,即全表扫描。为什么不使用索引?

作为一个实验,我测试了以下 SQL 命令:

SELECT DISTINCT
 "dynamic_col_1" "AS_dynamic_col_1", "dynamic_col_1_text" "AS_dynamic_col_1_text"
 FROM "tab"

即使在这种情况下,也不会使用索引,而是执行全表扫描。

在我的真实数据库中,该表包含更多索引列,例如“dynamic_col_1”和“dynamic_col_1_text”。 整个索引文件的大​​小约为 50 GB。

更多信息:

  • 数据库是安装在我的本地计算机上的 Oracle 11g。
  • 我使用的是 Windows 7 Enterprise 64bit。
  • 整个索引分为 3 个 dbf 文件,大小约为 50GB。

如果有人能告诉我如何让 Oracle 在第一个查询中使用索引,我真的很高兴。 因为第一个查询是被另一个程序用来从数据库中提取数据的,所以很难改变它。所以最好调整一下表格。

提前致谢。

[01.10.2011:更新]

我想我已经找到了问题的解决方案。 dynamic_col_1dynamic_col_1_text 两列都可以为空。在更改表以禁止两列中的“NULL”值并仅为列year 添加新索引后,Oracle 执行快速索引扫描。 优点是现在执行查询需要大约 5 秒,而不是像以前那样需要 1 小时。

【问题讨论】:

  • rowid 字段是主键吗?
  • 请不要调用表中的列 rowid,如果您需要使用实际的 pseudo-column rowid,那您是在要求一个痛苦的世界。
  • @BogdanSahlean 是的,rowid 是主键
  • @Ben 我知道这是个坏名字 ;-)。我的数据库中的列有其他名称。我只是以这种方式命名列以指示主键的位置。

标签: database oracle indexing


【解决方案1】:

我不知道它是否相关,但我测试了以下查询:

SELECT DISTINCT
"dynamic_col_1" "AS_dynamic_col_1", "dynamic_col_1_text" "AS_dynamic_col_1_text"
FROM "tab"
WHERE "dynamic_col_1" = 123 AND "dynamic_col_1_text" = 'abc'

该查询的解释计划表明 Oracle 在这种情况下使用索引扫描。

dynamic_col_1dynamic_col_1_text 列可以为空。这对索引的使用有影响吗?

01.10.2011:更新]

我想我已经找到了问题的解决方案。列 dynamic_col_1 和 dynamic_col_1_text 都可以为空。在更改表以禁止两列中的“NULL”值并仅为列年份添加新索引后,Oracle 执行快速索引扫描。优点是现在执行查询需要大约 5 秒,而不是像以前那样需要 1 小时。

【讨论】:

    【解决方案2】:

    您的第二个测试查询:

    SELECT DISTINCT
    "dynamic_col_1" "AS_dynamic_col_1", "dynamic_col_1_text" "AS_dynamic_col_1_text"
    FROM "tab"
    

    不会使用索引,因为您没有 WHERE 子句,因此您要求 Oracle 读取表中的每一行。在这种情况下,全表扫描是更快的访问方法。

    另外,正如其他发帖人所提到的,您在 YEAR 上的索引在第二列中有它。 Oracle 可以通过执行跳过扫描来使用此索引,但这样做会影响性能,并且根据表的大小,Oracle 可能会决定再次使用 FTS。

    【讨论】:

      【解决方案3】:

      试试这个:

      1) 在年份字段上创建索引(请参阅 Ollie 答案)。

      2) 然后使用这个查询:

      SELECT DISTINCT
      dynamic_col_1
      ,dynamic_col_1_text
      FROM tab 
      WHERE ID (SELECT ID FROM tab WHERE year=2011)
      

      SELECT DISTINCT
      dynamic_col_1
      ,dynamic_col_1_text
      FROM tab 
      WHERE ID (SELECT ID FROM tab WHERE year=2011)
      GROUP BY dynamic_col_1, dynamic_col_1_text
      

      也许对你有帮助。

      【讨论】:

        【解决方案4】:

        您确定索引访问会比全表扫描更快吗?作为一个非常粗略的估计,全表扫描比读取索引快 20 倍。如果tab 拥有超过 2011 年 5% 的数据,那么 Oracle 会使用全表扫描也就不足为奇了。正如@Dan 和@Ollie 所提到的,将year 作为第二列,这将使索引更慢。

        如果索引确实更快,那么问题可能是统计数据不佳。统计数据可能有数百种不好的方式。非常简单,这是我首先要看的内容:

        1. 运行带有和不带有索引提示的解释计划。基数是否减少了 10 倍或更多?休息时间是 10 倍或更多吗?
        2. 如果基数关闭,请确保表和索引上有最新的统计信息,并且您使用的是合理的 ESTIMATE_PERCENT(DBMS_STATS.AUTO_SAMPLE_SIZE 几乎总是最适合 11g)。
        3. 如果时间已过,请检查您的工作负载统计信息。
        4. 您是否使用并行性? Oracle 始终假设并行度接近线性改进,但在具有一个硬盘驱动器的桌面上,您可能根本看不到任何改进。

        此外,这与您的问题并不真正相关,但您可能希望避免使用带引号的标识符。一旦使用它们,您就必须在任何地方使用它们,这通常会使您的表和查询难以处理。

        【讨论】:

        • +1,过时的统计数据是许多性能问题的原因。关于 OP 也将访问的记​​录百分比的好消息。如果 FTS 正在检索相当比例的表记录,则 FTS 实际上可能是性能最高的访问方法。
        • @Ollie 好吧,如果我错了,请纠正我,但我认为在“dynamic_col_1 和 dynamic_col_1_text”两列上使用索引会加快“选择不同的 dynamic_col_1,dynamic_col_1_text ...”查询。不幸的是,我几乎无法更改 sql 查询,因为它是由另一个工具生成的,用于从表中提取数据。所以,那些带引号的标识符是不可避免的。
        • @oracle_user54 Ollie 和 jonearles 的观点是索引并不总是比 FTS 快。如果要提取的行的百分比足够大,则 FTS 会更快,因为(大致)来回访问索引的开销大于不扫描其余行的速度增益。
        【解决方案5】:

        你的索引应该是:

        CREATE INDEX Index_year 
        ON tab (year) 
        TABLESPACE tabspace_index;
        

        另外,您的查询可能只是:

        SELECT DISTINCT
               dynamic_col_1 "AS_dynamic_col_1",
               dynamic_col_1_text "AS_dynamic_col_1_text"
          FROM tab
         WHERE year = 2011;
        

        如果你的索引是专门为这个查询创建的,你也可以创建它,包括两个提取的列,那么优化器就不必去表中查询数据,它可以直接从索引中检索它再次让您的查询更有效率。

        希望对你有帮助...

        【讨论】:

        • 假设@jonearles 不正确并且索引会有所帮助,他会使用您的查询(我会这样做)。最好的索引是year, dynamic_col_1, dynamic_col_1_text,因为之后他不需要按rowid(实际的rowid)重新访问表。他可以从索引中获取所有数据。
        • @Ollie 我已经考虑过了。但是如果我从查询中完全删除列year并执行SELECT DISTINCT "dynamic_col_1" "AS_dynamic_col_1", "dynamic_col_1_text" "AS_dynamic_col_1_text" FROM "tab",则也不会使用索引。
        • @Ben 上面的例子应该只是说明问题。在真实的数据库中,有超过 200 个列 dynamic_col_1dynamic_col_1_text 必须被索引。我想,使用超过 500 列的 BIG 索引不是一个好主意?!
        • @oracle_user54 500 列索引是一个非常非常糟糕的 id。你真的不应该在一个表中有那么多列。阅读 jonearles 关于统计的回答。完成此操作后,请尝试使用/*+ index */ 提示将其降低索引。如果速度较慢,那么 jonearles 绝对是正确的,您需要做一个 FTS。我有时会强制使用 FTS,因为它们的速度要快得多。
        • @oracle_user54,查询获取的大约占总表记录的百分比?
        【解决方案6】:

        我手头没有 Oracle 实例,所以这有点猜测,但我倾向于说这是因为您的复合索引顺序错误。如果您将year 作为索引中的第一列,它可能会使用它。

        【讨论】:

          猜你喜欢
          • 2016-07-31
          • 1970-01-01
          • 2016-05-03
          • 1970-01-01
          • 1970-01-01
          • 2016-10-08
          • 2016-04-09
          • 2015-10-04
          • 1970-01-01
          相关资源
          最近更新 更多