2011-09-24 34 views
7

我的問題涉及Oracle 11g和SQL查詢中索引的使用。Oracle 11g:「select distinct」查詢中未使用的索引

在我的數據庫中,有被構造爲遵循表:

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使用「Table Full Access」,即全表掃描。爲什麼索引不被使用?

作爲實驗,我測試了以下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個大小爲50GB的dbf文件。

我真的很高興,如果有人能告訴我如何讓Oracle在第一個查詢中使用索引。 因爲第一個查詢被另一個程序用來從數據庫中提取數據,所以很難改變它。所以最好調整表格。

在此先感謝。

[2011年1月10日:更新]

我想我已經找到了問題的解決方案。列dynamic_col_1dynamic_col_1_text都是可空的。在更改表格以禁止兩列中的「NULL」值並僅爲列year添加新索引後,Oracle執行快速索引掃描。 好處是,查詢現在需要大約5秒鐘執行,而不是像以前那樣1小時。

+0

'rowid'字段是主鍵? –

+0

請不要在表rowid中調用列,如果您需要使用實際的[僞列rowid](http://download.oracle.com/docs/cd /B19306_01/server.102/b14200/pseudocolumns008.htm) – Ben

+0

@BogdanSahlean是的,rowid是主鍵 –

回答

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是可以爲空的。這是否會影響索引的使用?

2011年10月1日:UPDATE]

我想我已經找到了問題的解決方案。這兩列dynamic_col_1和dynamic_col_1_text都可以爲空。在更改表格以禁止兩列中的「NULL」值並僅爲列年添加新索引後,Oracle執行快速索引掃描。優點是查詢現在需要大約5秒鐘執行,而不是像以前那樣1小時。

2

我手邊沒有Oracle實例,所以這有點臆測,但我的傾向是說這是因爲你有複合索引的順序錯誤。如果您有year作爲索引中的第一列,它可能會使用它。

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; 

如果你的指數是此查詢單獨創作雖然,你可以創建它包括兩個牽強列,那麼優化器就不必爲查詢數據轉到表,它可以直接從索引中檢索它,使查詢再次更有效。

希望它可以幫助...

+1

假設@jonearles不正確,索引會幫助他,並使用你的查詢(我會)。最好的索引是'year,dynamic_col_1,dynamic_col_1_text',因爲他不需要通過rowid(實際的rowid)重新訪問表。他可以從索引中獲取所有數據。 – Ben

+0

@Ollie我已經想過了。但是,如果我從查詢中完全刪除列「year」並執行「S​​ELECT DISTINCT」dynamic_col_1「」AS_dynamic_col_1「,」dynamic_col_1_text「」AS_dynamic_col_1_text「FROM」tab「',則不會使用索引。 –

+0

@Ben上面的例子只能說明問題。在真實的數據庫中,那些需要索引的列的'dynamic_col_1'和'dynamic_col_1_text'超過200個。我想,使用一個超過500列的BIG索引不是好主意?! –

5

你確定一個索引訪問會比全表掃描速度更快?作爲一個非常粗略的估計,全表掃描比讀取索引要快20倍。如果tab在2011年有超過5%的數據,那麼Oracle會使用全表掃描並不奇怪。正如@丹和@奧利提到的那樣,year作爲第二列,這將使索引更慢。

如果索引真的比較快,那麼問題可能是不好的統計。統計數據可能有數百種不好。簡而言之,以下是我首先要看的內容:

  1. 運行有和無索引提示的解釋計劃。基數是10倍還是更多?時間是10倍還是更多?
  2. 如果基數關閉,請確保表和索引上有最新的統計信息,並且您使用了合理的ESTIMATE_PERCENT(DBMS_STATS.AUTO_SAMPLE_SIZE幾乎總是11g的最佳狀態)。
  3. 如果時間到了,請檢查您的工作負載統計信息。
  4. 你在使用並行性嗎?對於並行性,Oracle總是採取接近線性的改進,但在具有一個硬盤驅動器的桌面上,您可能根本看不到任何改進。

此外,這與您的問題並不真正相關,但您可能希望避免使用帶引號的標識符。一旦你使用它們,你必須在任何地方使用它們,它通常會讓你的表格和查詢很難處理。

+0

+1,過時的統計數據是許多性能問題的原因。有關OP將要訪問的記錄的百分比的好消息。如果他正在檢索表格記錄的合理百分比,FTS實際上可能是性能最高的訪問方法。 – Ollie

+0

@Ollie那麼,糾正我,如果我錯了,但我認爲使用兩列「dynamic_col_1和dynamic_col_1_text索引將加快」選擇不同dynamic_col_1,dynamic_col_1_text ...「查詢。不幸的是,我幾乎不能改變sql查詢,因爲它是由另一個工具從表中提取數據而產生的,所以,那些帶引號的標識符是不可避免的。 –

+0

@ oracle_user54奧利和jonearles指向的點是索引並不總是比FTS更快。如果您要拉動的行的百分比足夠大,FTS會更快,因爲(大致)來回索引的開銷大於不掃描其餘行的速度增益。 – Dan

0

試試這個:

1)創建同比領域的指數(見奧利答案)。

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 

也許這會幫助你。

1

你的第二個測試查詢:因爲你有

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

不會使用索引沒有WHERE子句,所以你問甲骨文在讀取表的每一行。在這種情況下,全表掃描是更快的訪問方法。

另外,正如其他海報所提到的,您在YEAR上的索引在第二欄中有。 Oracle可以通過執行跳過掃描來使用此索引,但這樣做會降低性能,並且根據表的大小,Oracle可能會決定再次使用FTS。