2010-03-30 145 views
1

我需要幫助優化以下查詢。完成需要很長時間。它需要近213秒。由於一些限制,我無法添加索引並必須與現有索引一起使用。需要幫助優化oracle查詢

INSERT INTO temp_table_1 
(USER_ID, role_id, participant_code, status_id) 
WITH A AS 
(SELECT USER_ID user_id,ROLE_ID, STATUS_ID,participant_code 
    FROM USER_ROLE WHERE participant_code IS NOT NULL), --1 
B AS 
(SELECT ROLE_ID 
    FROM CMP_ROLE 
    WHERE GROUP_ID = 3), 
C AS (SELECT USER_ID FROM USER) --2 

SELECT USER_ID,ROLE_ID,PARTICIPANT_CODE,MAX(STATUS_ID) 
FROM A INNER JOIN B USING (ROLE_ID) 
     INNER JOIN C USING (USER_ID) 
GROUP BY USER_ID,role_id,participant_code ; 

--1 = query when ran alone takes 100+ seconds 

--2 = query when ran alone takes 19 seconds 

DELETE temp_table_1 
WHERE ROWID NOT IN 
(SELECT a.ROWID 
    FROM temp_table_1 a, 
    USER_ROLE b 
    WHERE a.status_id = b.status_id 
    AND (b.ACTIVE IN (1) OR (b.ACTIVE IN (0,3) 
    AND SYSDATE BETWEEN b.effective_from_date AND b.effective_to_date)) 
); 

看起來好像編寫查詢的人試圖首先將所有內容放入臨時表中,然後從臨時表中刪除記錄。剩下的就是實際結果。

不能這樣做,不需要刪除?我們只是得到所需的結果,因爲這將節省時間?

+1

你可以發佈解釋計劃輸出? – 2010-03-30 20:55:24

回答

2

這是一個查詢,它天真地組合了上面的兩個查詢,因此請確保您檢查並比較兩個方法的輸出。

select 
    r.user_id, r.role_id, r.participant_code, max(status_id) 
from 
    user_role r, 
    cmp_role c 
where 
     r.role_id = c.role_id 
    and r.active in (0,1,3) 
    and r.participant_code is not null 
    and sysdate between r.effective_from_date and r.effective_to_date 
    and c.group_id = 3 
group by 
    r.user_id, r.role_id, r.participant_code; 

這是沒有必要使用臨時表,然後刪除記錄後獲得所需的結果。雖然,它可能有其使用的原因,可能是性能?

此外,它看起來像查詢並加入到USER表是不必要的,因爲USER_ID可從USER_ROLES獲得。我從上面的查詢中省略了它。希望能給你一個好的開始來改進它。

+0

unfortunatley,'USER'表的'USER_ID'多於'USER_ROLES'表。我將很快得到解釋計劃 – deming 2010-03-30 21:07:11

+0

@deming - 連接是一個*內部連接*,因此它將僅返回*表中的*行。只有當您的系統不強制執行外鍵時才需要加入USER。 – APC 2010-03-31 07:19:47

+0

+1這絕對是我如何處理同樣的問題。 – APC 2010-03-31 07:20:44

0

這應該在語義上等同於在現有代碼中刪除之後保留在臨時表中的集合。雖然我會同意AR,但是User表不是必需的,除非它包含的user_id比user_role少。否則它不會以任何方式限制該設置。如果用戶比user_role包含更多的user_id,則不會更改結果集。 User_role是此查詢中的主要驅動程序,具有來自cmp_role表的小限制。

select a.user_id, 
     a.role_id, 
     a.participant_code, 
     a.status_id 
    from (select a.user_id, 
       a.role_id, 
       a.participant_code, 
       max(status_id) status_id 
      from user_role a, 
       (select role_id 
        from cmp_role 
       where group_id = 3 
       ) b 
     where a.participant_code is not null 
      and a.active in (0, 1, 3) 
      and sysdate between a.effective_from_date and a.effective_to_date 
      and a.role_id = b.role_id 
     group by a.user_id, 
        a.role_id, 
        a.participant_code 
     ) a 
     user c 
where a.user_id = c.user_id; 

如果性能仍然很差,那麼人們可能看指標上一些用於限制數據(user_role.role_id,user_role.participant_code,user_role.active,user_role.effective_from_date,USER_ROLE字段.effective_to_date)。

當然,需要一個解釋計劃或跟蹤來獲得有關Oracle根據您的數據和結構執行此查詢時正在執行的操作的完整故事。

0

讓我們指出一些明顯的事情。

--1 =查詢時,獨自跑到花費超過100秒

當單獨運行需要19秒

用戶表上的全表掃描不應該採取--2 =查詢19秒。對USER_ROLE表的全表掃描不應超過100秒,即使它有數千萬行。當然,如果你真的有兩千萬用戶,那麼這些時間稍微不合理,但仍然不能接受。

您需要了解爲什麼需要您的系統進行簡單的查找。 EXPLAIN PLAN會希望我們理解這些連接,但這不會解決您的核心問題:爲什麼檢索USER_ROLE數據需要很長時間?這是一個複雜的觀點嗎?它有數以百萬計的查詢嗎?你有PARTICIPANT_CODE上的索引對這個查詢沒有幫助嗎?

使用這些表的其他查詢呢?他們也有問題嗎?如果是這樣,你需要調查更多。系統要花太長時間才能完成某項工作,或者它正在等待某些資源。你需要做的是對這個查詢運行一個10046跟蹤並確定時間到了哪裏。此跟蹤將報告您的會話的等待事件。這將給你一些體面的信息,以進行。這比猜測好得多。

自9i起Oracle已經暴露了Wait接口。羅傑施拉格寫了一篇相當不錯的介紹。 Read it now。 (如果你在10克或更高,你也應該讀his follow-up article)。