2010-04-01 141 views
1

我很難繞過這個查詢。它需要將近200多秒才能執行。我也粘貼了執行計劃。優化oracle查詢

SELECT  
     user_id , 
     ROLE_ID    , 
     effective_from_date , 
     effective_to_date , 
     participant_code  , 
     ACTIVE 
FROM  
     CMP_USER_ROLE E 
WHERE 
     ACTIVE = 0 
AND (SYSDATE BETWEEN effective_from_date AND effective_to_date 
     OR TO_CHAR(effective_to_date,'YYYY-Q') = '2010-2') 
AND participant_code = 'NY005' 
AND NOT EXISTS 
      (SELECT 1 FROM CMP_USER_ROLE r 
       WHERE r.USER_ID= E.USER_ID 
       AND r.role_id = E.role_id 
       AND r.ACTIVE = 4 
       AND E.effective_to_date 
         <= (SELECT MAX(last_update_date) 
          FROM CMP_USER_ROLE S 
          WHERE S.role_id = r.role_id 
          AND S.role_id = r.role_id 
          AND S.ACTIVE = 4)) 

解釋計劃

----------------------------------------------------------------------------------------------------- 
| Id | Operation      | Name    | Rows | Bytes | Cost (%CPU)| Time  | 
----------------------------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT     |     |  1 | 37 | 154 (2)| 00:00:02 | 
|* 1 | FILTER       |     |  |  |   |   | 
|* 2 | TABLE ACCESS BY INDEX ROWID | USER_ROLE  |  1 | 37 | 30 (0)| 00:00:01 | 
|* 3 | INDEX RANGE SCAN    | N_USER_ROLE_IDX6 | 27 |  |  3 (0)| 00:00:01 | 
|* 4 | FILTER       |     |  |  |   |   | 
| 5 | HASH GROUP BY     |     |  1 | 47 | 124 (2)| 00:00:02 | 
|* 6 |  TABLE ACCESS BY INDEX ROWID | USER_ROLE  | 159 | 3339 | 119 (1)| 00:00:02 | 
| 7 |  NESTED LOOPS    |     | 11 | 517 | 123 (1)| 00:00:02 | 
|* 8 |  TABLE ACCESS BY INDEX ROWID| USER_ROLE  |  1 | 26 |  4 (0)| 00:00:01 | 
|* 9 |  INDEX RANGE SCAN   | N_USER_ROLE_IDX5 |  1 |  |  3 (0)| 00:00:01 | 
|* 10 |  INDEX RANGE SCAN   | N_USER_ROLE_IDX2 | 957 |  | 74 (2)| 00:00:01 | 
----------------------------------------------------------------------------------------------------- 

統計:

Statistics 
---------------------------------------------------------- 
      0 recursive calls 
      0 db block gets 
    3433602 consistent gets 
      0 physical reads 
      0 redo size 
     58149 bytes sent via SQL*Net to client 
     1260 bytes received via SQL*Net from client 
     148 SQL*Net roundtrips to/from client 
      0 sorts (memory) 
      0 sorts (disk) 
     2199 rows processed 
+0

一個問題可能是子查詢中的加倍連接(倒數第二行)。刪除'AND S.role_id = r.role_id',看看它是否加快速度。 – 2010-04-01 17:07:15

+0

:)沒有注意到。但這並沒有多大幫助 – deming 2010-04-01 17:20:16

+0

您在該表上有哪些現有索引? – 2010-04-03 19:04:19

回答

0

第一件事是分析你的表:

EXEC dbms_stats.gather_table_stats('YOUR_SCHEMA', 'CMP_USER_ROLE'); 

你仍然可以得到相同的執行計劃?

執行計劃中的Time列看起來好像對Oracle優化器而言您的查詢看起來並不昂貴。

+0

in sql +?它說沒有找到dbms_stats命令.. – deming 2010-04-01 17:14:06

+0

請參閱更新。我也提供了統計數據 – deming 2010-04-01 17:17:47

+0

請參閱我更新的SQL * Plus答案。 – 2010-04-01 17:28:55

0

嘗試在查詢上運行EXPLAIN PLAN並查看它是否正在執行表掃描。

我猜想,這個條款是有問題的:

OR TO_CHAR(effective_to_date,'YYYY-Q') = '2010-2') 

我想呼籲在WHERE子句部隊甲骨文的功能來掃描每一行,因爲它必須評估每列功能,以看看這一行是否是結果集的一部分。這樣就會使索引無效。

更好的解決方案是對不需要函數調用的索引列值進行搜索來評估它。我建議像「DATE_COLUMN BETWEEN x AND y」這樣的東西,其中x和y是該季度的開始和結束日期。確保DATE_COLUMN上有一個索引。

+0

我也這麼認爲,因爲函數是在每個get上完成的,但是如何避免這種情況呢我應該按日期搜索宿舍,而不是使用函數/ – deming 2010-04-01 17:23:04

+0

@deming:''='和'<='(或'BETWEEN')替換'TO_CHAR'應該允許使用索引,所以我會試試。 – 2010-04-01 19:31:22

+0

'TRUNCATE(effective_to_date)BETWEEN DATE'2010- 01-01'AND DATE'2010-03-31'' – 2010-04-01 19:35:31

3

嗯,我的攻擊是deja vu

無論如何,這裏是你需要去努力的事情:

3433602 consistent gets 

三百萬邏輯IO是要咀嚼很多時間,所以你需要做的是減少數量。

您的查詢由針對同一個表的三組訪問組成。每個訪問由一個索引讀取和一個表讀取組成。從your comment to Peter's question看來你的統計數據相當準確(查詢返回699415行,NUM_ROWS = 697608)。

調整是一項複雜的活動,需要考慮很多因素。我們可以輕鬆地花費半天的時間瀏覽所有可能錯誤的事情。

例如,你是否爲你的索引和表格收集統計數據(在早期版本的Oracle中默認不收集索引統計數據)?如果你有統計數據那些指數的聚類因子是什麼?所有索引訪問都是掃描,因此聚類因子是相關的。

我們想要的是低聚集因子,因爲這意味着索引必須做更少的工作才能從表中獲取行。如果聚類因子更接近於索引中不合格的條目數量,而與表格中合適的塊數更接近。唉,鑑於你正在經歷的LIO的數量,我的錢是在可憐的集羣因素。所以你需要從索引中獲得更多的果汁。

查看您的查詢,最外側投影中的列用於查詢和/或子查詢的WHERE子句中。儘管如此,您正在使用三個不同的索引,並且它們都不提供滿足標準所需的所有信息(因此,附加的表讀取和後續過濾)。在這些情況下可以非常有效的一種策略是建立一個包含所有必要列的超級索引。

create index N_USER_ROLE_IDX23 on user_role  
     (active 
     , role_id 
     , user_id 
     , participant_code 
     , effective_from_date 
     , effective_to_date 
     , last_update_date) 

這會導致ACTIVE和ROLE_ID,因爲這些列在所有三組條件中都使用。 (順便說一下你的第三個查詢這樣說:

     WHERE S.role_id = r.role_id 
         AND S.role_id = r.role_id 

是正確的嗎?)無論如何,該指數的一點是,它滿足所有三個WHERE子句和最終投射,所以省卻了觸摸表中所有。因此可以顯着減少一致性獲取的數量。