2010-08-06 36 views
1

我無法獲得正在查找的正確查詢(oracle)。 基本上我想要的是:使用sql查詢奇怪的速度更改

SELECT count(ck.id) 
FROM claim_key ck 
WHERE (ck.dte_of_srvce > (SYSDATE - INTERVAL '30' DAY)) 
    AND ck.clm_type = 5 
    AND ck.prgrm_id = 1 

解釋:

 
| Id | Operation     | Name   | Rows | Bytes | Cost | 
| 0 | SELECT STATEMENT    |     |  1 | 14 | 3080| 
| 1 | SORT AGGREGATE    |     |  1 | 14 |  | 
| 2 | TABLE ACCESS BY INDEX ROWID| CLAIM_KEY  | 6531 | 91434 | 3080| 
| 3 | INDEX SKIP SCAN   | I_CLAIM_KEY_001 | 1306K|  | 2813| 

此查詢獲取我我想要的(平均爲20的結果),但需要10分鐘才能運行。

下面的查詢是不是很完整,但運行速度更快:

SELECT count(ck.id) 
FROM claim_key ck 
WHERE (ck.dte_of_srvce > (SYSDATE - INTERVAL '30' DAY)) 
    AND ck.clm_type = 5 

解釋:

 
| Id | Operation   | Name  | Rows | Bytes | Cost | 
| 0 | SELECT STATEMENT  |    |  1 | 11 | 9195 | 
| 1 | SORT AGGREGATE  |    |  1 | 11 |  | 
| 2 | TABLE ACCESS FULL | CLAIM_KEY | 19592 | 210K| 9195 | 

這將返回大約20爲好,雖然這只是僥倖,我可以」倚賴它,我需要包括prgrm_id。事情是,它只需要20秒。

下面的查詢是不是我要找的,但給人的性能的想法:

SELECT count(ck.id) 
FROM claim_key ck 
WHERE (ck.dte_of_srvce > (SYSDATE - INTERVAL '30' DAY)) 
 
| Id | Operation    | Name   | Rows | Bytes | Cost | 
| 0 | SELECT STATEMENT  |     |  1 |  8 |  4 | 
| 1 | SORT AGGREGATE  |     |  1 |  8 |  | 
| 2 | INDEX FAST FULL SCAN| I_CLAIM_KEY_002 | 195K| 1530K|  4 | 

這也需要20秒,但它返回的平均700條記錄。表claim_key大約有2500萬行。

該表上有多個索引。他們是:

 
IX_CLAIM_KEY_CREATED: CREATED_ON 
I_CLAIM_KEY_001: CLNC_STE_ID, PRVDR_ID, PRGRM_ID, UPDATED_ON 
I_CLAIM_KEY_002: SRCE_ID, PRVDR_ID, CLNC_ID, DTE_OF_SRVCE, PRGRM_ID 
I_CLAIM_KEY_003: CLNT_ID, DTE_OF_SRVCE 
I_CLAIM_KEY_004: TRNSMSN_ID, CLM_STTS 
I_CLAIM_KEY_005: UPDATED_ON 
I_CLAIM_KEY_006: PRVDR_ID, CMN_SRCE_ID 
PK_CLAIM_ID: ID 

我想知道的是爲什麼添加prgrm_id減慢這麼多?我預料它會很快,因爲它只需要搜索(ck.dte_of_srvce > (SYSDATE - INTERVAL '30' DAY))指定的700行。這是一個不正確的假設嗎?

編輯

在第一查詢中使用的提示/*+ FULL(ck) */,它的執行時間下降,並且它會產生以下的計劃。

 
| Id | Operation   | Name  | Rows | Bytes | Cost | 
| 0 | SELECT STATEMENT  |    |  1 | 14 | 9195 | 
| 1 | SORT AGGREGATE  |    |  1 | 14 |  | 
| 2 | TABLE ACCESS FULL | CLAIM_KEY | 6531 | 91434 | 9195 | 
+1

有關Oracle查詢性能問題的標準註釋:如果您可以發佈上述每個查詢的執行計劃,那麼人們將獲得更多有用的信息。 – 2010-08-06 18:26:19

回答

4

爲了更好地理解這是怎麼回事,試試這個:

explain plan set statement_id = 'query1' for 
SELECT count(ck.id) 
FROM claim_key ck 
WHERE (ck.dte_of_srvce > (SYSDATE - INTERVAL '30' DAY)) 
    AND ck.clm_type = 5 
    AND ck.prgrm_id = 1; 

然後:

select * 
from table(dbms_xplan.display(statement_id=>'query1')); 

我猜你會看到指示線對claim_key表訪問已滿。

然後嘗試:

explain plan set statement_id = 'query2' for 
SELECT count(ck.id) 
FROM claim_key ck 
WHERE (ck.dte_of_srvce > (SYSDATE - INTERVAL '30' DAY)) 
    AND ck.clm_type = 5; 

select * 
from table(dbms_xplan.display(statement_id=>'query2')); 

,並檢查它(大概)使用的是什麼指數。這應該讓你知道數據庫在做什麼,這有助於弄清楚爲什麼它正在這樣做。


好吧,給你解釋的計劃,它是「索引不總是好的,表掃描並不總是壞」的典型例子。

INDEX SKIP SCAN是數據庫可以嘗試使用索引的地方,即使索引的前導列未被使用。基本上,如果你的指數看起來像這樣(簡化過度):

COL1 COL2 ROWID 
A  X  1  <-- 
A  Y  2 
A  Z  3 
B  X  4  <-- 
B  Y  5 
B  Z  6 

和你的條件是WHERE COL2 =「X」的索引跳躍式掃描表示通過COL1每個組合尋找其中COL2 =「X」。一旦找到一個匹配(例如col1 = A,col2 = X),直到col1的值發生變化(col1 = B,然後col1 = C等),它會跳過col1中的值並尋找更多匹配。

美中不足的是,指標(一般!)這樣的工作: 1)發現在指數)的下一個ROWID其中的值被發現 2去與ROWID(TABLE ACCESS BY INDEX ROWID表塊) 3)重複,直到找不到更多的匹配。

(在跳轉掃描的同時,也會招致找出其中的值的下一個變化是領先列的費用。)

這是一切都很好了少量的行,但受到收益遞減規律的影響;當你有大量的行時,它並不是那麼棒。這是因爲它必須讀取索引塊,然後是表格塊,然後是索引塊,表格塊(即使之前讀取表格塊)。

全表掃描只是「犁過」數據部分是......多塊讀取。數據庫可以在一次讀取中從磁盤讀取多個塊,並且不會多次讀取同一個塊。

INDEX FAST FULL SCAN基本上將I_CLAIM_KEY_002視爲表格。您在查詢中所需的全部內容都可以通過索引單獨回答;不需要TABLE ACCESS。 (我猜I_CLAIM_KEY_002被定義爲clnt_id,dte_of_srvce,並且clnt_id或dte_of_srvce不能爲空。由於ck.id應該是非空屬性,因此ck.id的計數與ck.clnt_id的計數相同。)

,以便爲您的初始查詢,除非你想rejig你的索引,試試這個:

SELECT /*+ FULL(ck) */ count(ck.id) 
FROM claim_key ck 
WHERE (ck.dte_of_srvce > (SYSDATE - INTERVAL '30' DAY)) 
    AND ck.clm_type = 5 
    AND ck.prgrm_id = 1 

這將迫使claim_key(CK)進行全表掃描,你可以看到類似的性能與其他二。(檢查這是第一種情況,首先在「explain plan set statement_id ='query_hint'for」前加上查詢前綴並在運行之前運行dbms_xplan查詢。)

(現在你會問「我想把在這樣的提示中「?請不要,這只是一個測試,這只是爲了檢查FTS是否比INDEX SKIP SCAN更好,如果是,那麼你需要找出原因。:)

反正......我希望做成snese ..我的意思是說。

+0

嘿,當運行第一個查詢的後半部分時,我會在「=」下​​找到「缺少右括號」的錯誤?我不熟悉你的語法,你知道爲什麼嗎? – 2010-08-06 17:08:52

+0

AFAIK你不能在SQL查詢中使用命名參數表示法。試試'select * from table(dbms_xplan.display('PLAN_TABLE','query1'))' – 2010-08-06 18:33:10

+0

上面的dbms_xplan語法實際上是在11g上測試的,所以我很抱歉,如果它不是向後兼容的話。@Dave Costa建議的語法可能是10g的方法 – 2010-08-06 19:18:53

1

在我看來,沒有任何在您的文章中顯示的索引是非常有用的。無論如何,我希望它能夠進行全表掃描。

如果可以的話,在

DTE_OF_SRVCE, CLM_TYPE, PRGRM_ID 

添加一個索引,看看有沒有什麼幫助。如果您想嘗試僅索引檢索,請將ID添加到索引的末尾(因此它將是DTE_OF_SRVCE,CLM_TYPE,PRGRM_ID,ID)。

分享和享受。

+0

感謝您的建議。但是,因爲我似乎總是無權創建索引。 – 2010-08-06 17:10:20

0

也許這是因爲cInt_id只在你的第三個索引上。和否則將使用第二個

0

也許在這裏指出明顯的,但這些結果是可重複的,或者你試過這一次,按照你的問題中指定的順序?如果是的話,塊緩存可以解釋這些差異。

+0

是的,它們是可重複的。對不起,我不熟悉塊緩存的含義? – 2010-08-06 17:09:36

+0

數據庫緩存了第一個查詢的結果。 – JNK 2010-08-06 17:21:30

+0

哦,好的。不,這絕對不是。 – 2010-08-06 17:31:50

0

,如果你嘗試類似,會發生什麼......

SELECT COUNT(*) 
    FROM (SELECT ck.id, 
       ck.prgrm_id AS prgrm_id 
       FROM claim_key ck 
       WHERE (ck.dte_of_srvce > (SYSDATE - INTERVAL '30' DAY)) AND 
        ck.clm_type = 5) AS sq 
    WHERE sq.prgrm_id = 1; 

我不能在家裏嘗試這樣的事情,因此它可能是沒有好,但它可能會有所幫助。

+0

我其實已經試過了。完全相同的結果,它仍然需要大約10分鐘運行。 – 2010-08-06 19:25:25

+0

什麼是CLAIM_KEY和I_CLAIM_KEY_001? – 2010-08-06 19:57:34