Oracle 11g:未在“select distinct”-query中使用的索引

时间:2011-09-24 13:54:11

标签: database oracle indexing

我的问题涉及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 64位。
  • 整个索引分为3个dbf文件,大小约为50GB。

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

提前致谢。

[01.10.2011:更新]

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

6 个答案:

答案 0 :(得分:5)

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

如果索引真的比较快,那么问题可能就是糟糕的统计数据。有数百种统计数据可能不好的方法。非常简短,这是我首先要看的内容:

  1. 使用和不使用索引提示运行解释计划。基数是否超过10倍?时间是10倍或更多吗?
  2. 如果基数已关闭,请确保表和索引上有最新的统计信息,并且您使用的是合理的ESTIMATE_PERCENT(DBMS_STATS.AUTO_SAMPLE_SIZE几乎总是最适合11g)。
  3. 如果时间已过,请检查工作量统计信息。
  4. 你在使用并行吗?对于并行性,Oracle总是假设接近线性的改进,但在具有一个硬盘的桌面上,您可能根本看不到任何改进。
  5. 此外,这与您的问题无关,但您可能希望避免使用带引号的标识符。一旦你使用它们,你必须在任何地方使用它们,它通常会使你的表和查询很难使用。

答案 1 :(得分:4)

你的索引应该是:

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;

如果您的索引是专门为此查询创建的,您可以创建它,包括两个已获取的列,然后优化器不必转到查询数据的表,它可以直接从索引中检索它使你的查询再次更有效率。

希望它有所帮助...

答案 2 :(得分:2)

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

答案 3 :(得分: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小时。

答案 4 :(得分:1)

您的第二个测试查询:

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。

答案 5 :(得分:0)

试试这个:

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

也许它会帮助你。