2011-09-09 34 views
0

我有以下查詢在Oracle10g中:如何索引表以優化此Oracle SELECT查詢?

select * 
    from DATA_TABLE DT, 
     LOOKUP_TABLE_A LTA, 
     LOOKUP_TABLE_B LTB 
where DT.COL_A = LTA.COL_A (+) 
    and DT.COL_B = LTA.COL_B (+) 
    and LTA.COL_C = LTB.COL_C 
    and LTA.COL_B = LTB.COL_B 
    and (DT.REF_TXT = :refTxt or DT.ALT_REF_TXT = :refTxt) 
    and DT.CREATED_DATE between :startDate and :endDate 

,並想知道你是否已經得到了優化查詢的任何提示。

目前我有以下指標:

IDX1 on DATA_TABLE (REF_TXT, CREATED_DATE) 
IDX2 on DATA_TABLE (ALT_REF_TXT, CREATED_DATE) 
LOOKUP_A_PK on LOOKUP_TABLE_A (COL_A, COL_B) 
LOOKUP_A_IDX1 on LOOKUP_TABLE_A (COL_C, COL_B) 
LOOKUP_B_PK on LOOKUP_TABLE_B (COL_C, COL_B) 

注意,查找表是非常小的(< 200行)。

編輯:

解釋計劃:

Query Plan 
SELECT STATEMENT Cost = 8 
    FILTER 
    NESTED LOOPS 
     NESTED LOOPS 
     TABLE ACCESS BY INDEX ROWID DATA_TABLE 
      BITMAP CONVERSION TO ROWIDS 
      BITMAP OR 
       BITMAP CONVERSION FROM ROWIDS 
       SORT ORDER BY 
        INDEX RANGE SCAN IDX1 
       BITMAP CONVERSION FROM ROWIDS 
       SORT ORDER BY 
        INDEX RANGE SCAN IDX2 
     TABLE ACCESS BY INDEX ROWID LOOKUP_TABLE_A 
      INDEX UNIQUE SCAN LOOKUP_A_PK 
     TABLE ACCESS BY INDEX ROWID LOOKUP_TABLE_B 
     INDEX UNIQUE SCAN LOOKUP_B_PK 

EDIT2:

的數據是這樣的:

有將不同REF_TXT的10000S,其中10-100s CREATED_DTs的爲每個。 ALT_REF_TXT將大部分爲NULL,但將會有100s-1000s,它將與REF_TXT不同。

EDIT3:修正ALT_REF_TXT實際包含的內容。

+3

你也許同樣會刪除'(+)'S,由於內從LTA加入到LTB殺死他們一起嘗試創建外。 –

+2

請提供解釋計劃 –

+6

我確實相信Oracle也有明確的連接語法,這是一種編碼外連接的隱藏方法。從1989年開始使用SQL '92語法。 – Johan

回答

2

這是它使得很少有意義的嘗試不知道你的數據意味着什麼,以優化性能DBMS的案件之一。

你有很多很多不同的CREATED_DATE值和幾排在DT每個日期?如果是這樣,您需要CREATED_DATE上的索引,因爲它將是DBMS拒絕不想處理的列的主要方式。

在另一方面,你只有日期屈指可數,而REF_TXT或ALT_REF_TXT的許多獨特的價值?在這種情況下,你可能有正確的複合索引選擇。

的或在您的查詢中的存在大大複雜的事情,並拋出最猜測出來的窗口。您必須看解釋計劃,看看發生了什麼。

如果你有幾千萬不同REF_TXT和ALT_REF_TXT值,你可能要考慮非規範化此架構。


編輯。 感謝您的額外信息。你的解釋計劃中沒有我能看到的吸菸槍。如果你對性能還不滿意,可以嘗試下一步。

翻轉數據表上覆合索引中列的順序。也許這會讓你更簡單的索引範圍掃描,而不是所有的位圖猴子業務。

將您的SELECT *換成您在查詢結果集中實際需要的列名稱。這在任何情況下都是很好的編程實踐,它可以允許優化器避免一些工作。

如果事情仍然太慢,嘗試重鑄這兩個查詢,而不是使用OR的UNION。這可以允許您的查詢的alt_ref_txt部分(該列由該列中的所有NULL值變得更復雜一些)分別進行優化。

2

這可能是你想使用更高達日期語法查詢。

(而且沒有內部連接斷裂外連接)

select 
    * 
from 
    DATA_TABLE DT 
left outer join 
    (
    LOOKUP_TABLE_A LTA 
    inner join 
    LOOKUP_TABLE_B LTB 
     on LTA.COL_C = LTB.COL_C 
     and LTA.COL_B = LTB.COL_B 
) 
    on DT.COL_A = LTA.COL_A 
    and DT.COL_B = LTA.COL_B 
where 
    (DT.REF_TXT = :refTxt or DT.ALT_REF_TXT = :refTxt) 
    and DT.CREATED_DATE between :startDate and :endDate 

指標,我不得不爲...

LOOKUP_TABLE_A (COL_A, COL_B) 
LOOKUP_TABLE_B (COL_B, COL_C) 
DATA_TABLE (REF_TXT, CREATED_DATE) 
DATA_TABLE (ALT_REF_TXT, CREATED_DATE) 


注:在WHERE的第一個條件子句中包含一個OR,可能會使INDEX的使用變得分裂。在這種情況下,我有UNIONing兩個查詢一起看到性能優勢...

<your query> 
where 
    DT.REF_TXT = :refTxt 
    and DT.CREATED_DATE between :startDate and :endDate 

UNION 

    <your query> 
where 
    DT.ALT_REF_TXT = :refTxt 
    and DT.CREATED_DATE between :startDate and :endDate 
+0

感謝您的更新語法。 – beny23

3

您目前得到看起來很不錯的執行計劃。沒有明顯的改進。

至於其他已經指出,你有一些外連接的指標,但你基本上是防止外連接,要求兩個外部表中的其他列平等。從執行計劃中可以看到,沒有發生外連接。如果您不想要外連接,請刪除運營商,他們只是混淆了這個問題。如果您確實需要外連接,請按照@Dems所示重寫查詢。

如果您對當前性能不滿意,我會建議使用gather_plan_statistics提示運行查詢,然後使用DBMS_XPLAN.DISPLAY_CURSOR(?,?,'ALLSTATS LAST')查看實際執行統計信息。這將顯示歸因於執行計劃中每個步驟的已用時間。

你可能會從轉換查找表的一個或兩個成索引組織表得到一些好處。在IDX1和IDX2

3

你2索引範圍掃描,將產生最多100行,所以您的位圖轉換的ROWID將最多200行產生。從那裏開始,它只能通過rowid進行索引訪問,從而導致可能的亞秒級執行。那麼你是否真的遇到性能問題?如果是這樣,需要多長時間?

如果您遇到性能問題,請遵循Dave Costa的建議並獲得真實計劃,因爲在這種情況下,您可能正在使用另一個計劃運行時,可能是由於某些綁定變量值或不同的優化器環境設置。

問候,
羅布。

0

使用「set autot trace」提供此查詢的輸出。讓我們看看它拉多少塊。解釋計劃看起來不錯,應該是非常快的。如果您需要更多,請將查找表信息非規範化爲DT。違反第三範式,但它會通過消除連接來加快查詢速度。在毫秒計數的情況下,一切都在緩衝區中,並且您需要該查詢以每秒1000次的速度運行,這可以通過降低每行查看的塊數來提供幫助。這是提高閱讀性能的最終方法,但會使您的應用程序變得複雜(並且會毀掉您可愛的ER圖)。