2012-10-19 88 views
1

好的,所以我正在研究這個(相當老的)項目,它使用了大量針對Oracle數據庫的查詢。我最近偶然發現了這個寶石,大約需要6-7小時小時才能運行並返回〜1400行。問題中的表格/視圖包含〜200'000行。我覺得這感覺好像比合理的時間稍長一點,所以我開始仔細觀察它。現在我不能,出於安全/特殊原因,共享準確的查詢,但這應該顯示查詢確實較一般什麼:驗證Oracle查詢的優化

SELECT 
    some_field, 
    some_other_field 
FROM (
    SELECT 
     * 
    FROM 
     some_view a 
    WHERE 
     some_criteria AND 
     a.client_no || ':' || a.engagement_no || ':' || a.registered_date = (
      SELECT 
       b.client_no || ':' || b.engagement_no || ':' || MAX(b.registered_date) 
      FROM 
       some_view b 
       JOIN some_engagement_view e 
        ON e.client_no = b.client_no AND e.engagement_no = b.engagement_no 
       JOIN some_client_view c 
        ON c.client_no = b.client_no 
      WHERE 
       some_other_criteria AND 
       b.client_no = a.client_no AND 
       b.engagement_no = a.engagement_no 
      GROUP BY 
       b.client_no, 
       b.engagement_no 
     ) 
); 

基本上什麼是應該做的,據我」我們設法弄清楚,是從some_view(其中包含對客戶/訂婚的評估)獲取每個獨特客戶/參與的最新評估。

這兩個連接可以確保客戶和參與在另一個系統中存在,在您完成系統評估後主要處理它們。

請注意它是如何連接兩個數字和日期,然後將其與子查詢進行比較? 「有趣」的設計選擇。所以我認爲如果用適當的比較來替換串聯,至少可以獲得某種性能增益。請注意,我主要開發.NET和網絡,以及當它涉及到數據庫的專家很遠,但我重寫,如下所示:

SELECT 
    some_field, 
    some_other_filed 
FROM 
    some_view a 
WHERE 
    some_criteria AND 
    (a.client_no, a.engagement_no, a.registered_date) = (
     SELECT 
      b.client_no, 
      b.engagement_no, 
      MAX(b.registered_date) 
     FROM 
      some_view b 
      JOIN some_engagement_view e 
       ON e.client_no = b.client_no AND e.engagement_no = b.engagement_no 
      JOIN some_client_view c 
       ON c.client_no = b.client_no 
     WHERE 
      some_other_criteria AND 
      b.client_no = a.client_no AND 
      b.engagement_no = a.engagement_no 
     GROUP BY 
      b.client_no, 
      b.engagement_no 
    ) 
); 

現在,如果我替換等領域都有了第一選擇與COUNT(1),我得到兩個查詢完全相同的行數,所以一個好的開始。新查詢提取數據的速度與其計數一樣快,< 10秒。舊的查詢在〜20秒內得到計數,正如我之前提到的那樣,數據需要接近6-7小時。它目前正在運行,因此我可以進行某種分析以查看新查詢是否有效,但是我認爲我也會在此問一下,看看我做了什麼明顯錯誤的事情?

編輯還刪除了最外面的查詢,這似乎並沒有履行任何樣的目的,除了可能使查詢外觀更酷..或東西..我不知道..

+1

爲避免自己加入'some_view',可能值得考慮[分析函數](http://docs.oracle.com/cd/E11882_01/server.112/e26088/functions004.htm)。但你應該看看解釋計劃,看看它花費的時間。 –

回答

1

擴大我的評論...如果我嘗試使用內置視圖複製您的查詢結構,它也會運行很長時間。例如,獲取最新創建的表中的每個所有者(純粹是爲了演示目的,它可以更簡單地完成),這樣需要幾分鐘,有兩個版本:

SELECT 
    owner, 
    object_name 
FROM 
    all_objects a 
WHERE 
    (a.owner, a.object_type, TRUNC(a.created)) = (
     SELECT 
      b.owner, b.object_type, TRUNC(MAX(b.created)) 
     FROM 
      all_objects b 
      JOIN all_tables e 
       ON e.owner = b.owner and e.table_name = b.object_name 
      JOIN all_users c 
       ON c.username = b.owner 
     WHERE 
      b.owner = a.owner AND 
      b.object_type = a.object_type 
     GROUP BY 
      b.owner, 
      b.object_type 
    ); 

如果我重寫,以避免本通過使用analytic function,而不是-join上all_objects(相當於some_view在你的例子):

SELECT 
    owner, 
    object_name 
FROM (
    SELECT 
     a.owner, 
     a.object_name, 
     row_number() over (partition by a.owner, a.object_type 
      order by a.created desc) as rn 
    FROM 
     all_objects a 
     JOIN all_tables e 
      ON e.owner = a.owner and e.table_name = a.object_name 
     JOIN all_users c 
      ON c.username = a.owner 
    ) 
WHERE 
    rn = 1; 

...那麼它需要幾秒鐘。

現在,在這種情況下,我得不到完全相同的輸出,因爲我有多個對象在同一時間創建(在同一秒內,就created而言)。

當然,我不知道registered_date中存儲的值的精確度。因此,您可能需要查看不同功能,可能是rank而不是row_number,或者調整排序以在必要時處理關係。

 rank() over (partition by a.owner, a.object_type 
      order by trunc(a.created) desc) as rn 
... 
WHERE 
    rn = 1; 

使我有同樣的結果(當然,幾乎;聯接到all_tables也歪斜的事情,因爲我似乎已經在all_objects上市不在all_tables表,但是這是一個方面的問題)。或者max可以工作了:

 max(created) over (partition by a.owner, a.object_type) as mx 
... 
WHERE 
    TRUNC(created) = TRUNC(mx) 

在這兩個我使用trunc獲得當天的一切;如果您的registered_date沒有時間組件,您可能不需要。

但當然,檢查你確實得到相同的結果。