2011-11-21 84 views
1

在LINUX上使用Oracle 10gR2時,我試圖調整以下查詢。
我敢肯定,擺脫相關的子查詢和一些分析函數的可能使用可能是最佳的方式去,但我只是沒有得到它 - 尤其是與嵌套相關子查詢選擇MAX(TABLE_2.NOTE_DATE)。任何幫助將非常感激。謝謝。在不使用相關子查詢的情況下重寫查詢

EXPLAIN PLAN FOR 
SELECT TABLE_4.INCIDENT_TYPE, 
    TABLE_4.POC_CONTACT, 
    (SELECT TABLE_2.NOTE_DATE 
      || ' ' 
      || TABLE_1.USER_FIRST_NAME 
      || ' ' 
      || TABLE_1.USER_LAST_NAME 
      || ' : ' 
      || TABLE_2.OTHER_HELP_NOTES 
    FROM TABLE_1, 
      TABLE_2 
    WHERE TABLE_2.USER_ID = TABLE_1.USER_ID 
      AND TABLE_2.REC_ID = TABLE_4.REC_ID 
      AND TABLE_2.NOTE_DATE = (SELECT MAX(TABLE_2.NOTE_DATE) 
               FROM TABLE_2 
               WHERE TABLE_2.REC_ID = TABLE_4.REC_ID 
                AND TABLE_2.NOTE_DATE <= 
                 TABLE_4.REPORT_DATE)) 
                   AS SUM_OF_SHORTAGE, 
    (SELECT TABLE_3.NOTE_DATE 
      || ' ' 
      || TABLE_1.USER_FIRST_NAME 
      || ' ' 
      || TABLE_1.USER_LAST_NAME 
      || ' : ' 
      || TABLE_3.HELP_NOTES 
    FROM TABLE_1, 
      TABLE_3 
    WHERE TABLE_3.USER_ID = TABLE_1.USER_ID 
      AND TABLE_3.REC_ID = TABLE_4.REC_ID 
      AND TABLE_3.NOTE_DATE = (SELECT MAX(TABLE_3.NOTE_DATE) 
                FROM TABLE_3 
                WHERE TABLE_3.REC_ID = TABLE_4.REC_ID 
                 AND TABLE_3.NOTE_DATE <= 
                  TABLE_4.REPORT_DATE)) AS HELP_NOTES, 
    TABLE_4.REPORT_NUM 
FROM TABLE_4 
WHERE TABLE_4.SITE_ID = '1'; 

@C:\ORACLE\PRODUCT\11.2.0\CLIENT_1\RDBMS\ADMIN\UTLXPLS.SQL; 

PLAN_TABLE_OUTPUT                                                                        
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 
PLAN HASH VALUE: 4036328474                                                                     

------------------------------------------------------------------------------------------------------------                                                 
| ID | OPERATION      | NAME      | ROWS | BYTES | COST (%CPU)| TIME  |                                                 
------------------------------------------------------------------------------------------------------------                                                 
| 0 | SELECT STATEMENT    |       | 13009 | 2286K| 449 (2)| 00:00:06 |                                                 
|* 1 | FILTER      |       |  |  |   |   |                                                 
| 2 | NESTED LOOPS    |       |  3 | 612 |  8 (0)| 00:00:01 |                                                 
| 3 | TABLE ACCESS BY INDEX ROWID| TABLE_2     |  3 | 552 |  5 (0)| 00:00:01 |                                                 
|* 4 |  INDEX RANGE SCAN   | IX_TABLE_2_REC_ID   |  3 |  |  1 (0)| 00:00:01 |                                                 
| 5 | TABLE ACCESS BY INDEX ROWID| TABLE_1     |  1 | 20 |  1 (0)| 00:00:01 |                                                 
|* 6 |  INDEX UNIQUE SCAN   | TABLE_1_PK     |  1 |  |  0 (0)| 00:00:01 |                                                 
| 7 | SORT AGGREGATE    |       |  1 | 13 |   |   |                                                 
|* 8 | TABLE ACCESS BY INDEX ROWID| TABLE_2     |  1 | 13 |  5 (0)| 00:00:01 |                                                 
|* 9 |  INDEX RANGE SCAN   | IX_TABLE_2_REC_ID   |  3 |  |  1 (0)| 00:00:01 |                                                 
|* 10 | FILTER      |       |  |  |   |   |                                                 
|* 11 | HASH JOIN     |       | 17 | 4063 | 482 (2)| 00:00:06 |                                                 
|* 12 | TABLE ACCESS FULL   | TABLE_3     | 17 | 3723 | 474 (2)| 00:00:06 |                                                 
| 13 | TABLE ACCESS FULL   | TABLE_1     | 1504 | 30080 |  8 (0)| 00:00:01 |                                                 
| 14 | SORT AGGREGATE    |       |  1 | 13 |   |   |                                                 
|* 15 | TABLE ACCESS FULL   | TABLE_3     |  1 | 13 | 474 (2)| 00:00:06 |                                                 
|* 16 | TABLE ACCESS FULL   | TABLE_4     | 13009 | 2286K| 449 (2)| 00:00:06 |                                                 
------------------------------------------------------------------------------------------------------------                                                 

PREDICATE INFORMATION (IDENTIFIED BY OPERATION ID):                                                               
---------------------------------------------------                                                               

    1 - FILTER("TABLE_2"."NOTE_DATE"= (SELECT /*+ */ MAX("TABLE_2"."NOTE_DATE")                                                   
       FROM "TABLE_2" "TABLE_2" WHERE "TABLE_2"."REC_ID"=:B1 AND                                                   
       "TABLE_2"."NOTE_DATE"<=:B2))                                                               
    4 - ACCESS("TABLE_2"."REC_ID"=:B1)                                                                
    6 - ACCESS("TABLE_2"."USER_ID"="TABLE_1"."USER_ID")                                                           
    8 - FILTER("TABLE_2"."NOTE_DATE"<=:B1)                                                               
    9 - ACCESS("TABLE_2"."REC_ID"=:B1)                                                                
    10 - FILTER("TABLE_3"."NOTE_DATE"= (SELECT /*+ */                                                            
       MAX("TABLE_3"."NOTE_DATE") FROM "TABLE_3" "TABLE_3" WHERE                                                
       "TABLE_3"."REC_ID"=:B1 AND "TABLE_3"."NOTE_DATE"<=:B2))                                                    
    11 - ACCESS("TABLE_3"."USER_ID"="TABLE_1"."USER_ID")                                                          
    12 - FILTER("TABLE_3"."REC_ID"=:B1)                                                               
    15 - FILTER("TABLE_3"."REC_ID"=:B1 AND "TABLE_3"."NOTE_DATE"<=:B2)                                                    
    16 - FILTER("TABLE_4"."SITE_ID"=1)                                                                
41 ROWS SELECTED 

打破這個查詢 - 關鍵問題似乎是以下幾點:

select REC_ID, TO_CHAR(REPORT_DATE,'DD-MON-YY HH:MI:SS') REPORT_DATE, 
(SELECT MAX(TABLE_2.note_date) as MAX_DATE 
FROM TABLE_2 
where TABLE_2.REC_ID = TABLE_1.REC_ID 
     and TABLE_2.NOTE_DATE <= TABLE_1.REPORT_DATE 
) NOTES_MAX_DATE 
from TABLE_1 where REC_ID = 121 order by TO_DATE(REPORT_DATE,'DD-MON-YY HH:MI:SS'); 

應返回如下:

REC_ID     REPORT_DATE  NOTES_MAX_DATE       
---------------------- ------------------ ------------------------- 
121     17-APR-10 12:30:00       
121     24-APR-10 12:30:00       
121     01-MAY-10 12:30:00       
121     08-MAY-10 12:30:00       
121     15-MAY-10 12:30:00 12-MAY-10     
121     22-MAY-10 12:30:01 17-MAY-10     
121     29-MAY-10 12:30:01 25-MAY-10     
121     05-JUN-10 12:30:00 25-MAY-10     
8 rows selected 

的輸出要求是一樣的以上。我試圖創建連接如下:

SELECT TABLE_1.REC_ID, TO_CHAR(TABLE_1.REPORT_DATE,'DD-MON-YY HH:MI:SS') REPORT_DATE, MAX(TABLE_2.NOTE_DATE) AS NOTES_MAX_DATE 
    FROM TABLE_2, 
      TABLE_1 
    where TABLE_2.REC_ID = TABLE_1.REC_ID 
      AND TABLE_2.NOTE_DATE <= TABLE_1.REPORT_DATE 
      and (TABLE_1.SITE_ID = '1') 
      and TABLE_1.REC_ID = 121 
    group by TABLE_1.REC_ID, TABLE_1.REPORT_DATE 
    order by TO_DATE(REPORT_DATE,'DD-MON-YY HH:MI:SS'); 

但是產量:

REC_ID     REPORT_DATE  NOTES_MAX_DATE   
---------------------- ------------------ ------------------------- 
121     15-MAY-10 12:30:00 12-MAY-10     
121     22-MAY-10 12:30:01 17-MAY-10     
121     29-MAY-10 12:30:01 25-MAY-10     
121     05-JUN-10 12:30:00 25-MAY-10     

所以我真的很爲難。有任何想法嗎? - 謝謝。

回答

2

下面是一個版本, 只獲得 max一次, 應刪除相關的子查詢。它仍然使用子查詢,但是,因爲它們在FROM子句而不是SELECT子句中,所以數據庫應該更好地解決它們。這可能也可以刪除這些子查詢,但這樣更具可讀性。此版本還爲連接使用SQL-99語法,這通常被認爲是更可取的。

SELECT table_4.incident_type, 
     table_4.poc_contact, 
     t2.sum_of_shortage, 
     t3.help_notes, 
     table_4.report_num 
FROM    table_4 
     LEFT JOIN (SELECT table_2.rec_id, 
         table_2.note_date 
         || ' ' 
         || table_1.user_first_name 
         || ' ' 
         || table_1.user_last_name 
         || ' : ' 
         || table_2.other_help_notes 
          AS sum_of_shortage 
        FROM table_1 
        JOIN table_2 
         ON table_2.user_id = table_1.user_id 
        WHERE table_2.note_date = 
          (SELECT MAX(table_2.note_date) AS max_date 
          FROM table_2 
          WHERE table_2.rec_id = table_4.rec_id 
           AND table_2.note_date <= table_4.report_date)) t2 
       ON t2.rec_id = table_4.rec_id 
     LEFT JOIN (SELECT table_3.rec_id, 
         table_3.note_date 
         || ' ' 
         || table_1.user_first_name 
         || ' ' 
         || table_1.user_last_name 
         || ' : ' 
         || table_3.other_help_notes 
          AS help_notes 
        FROM table_1 
        JOIN table_3 
         ON table_3.user_id = table_1.user_id 
        WHERE table_2.note_date = 
          (SELECT MAX(table_3.note_date) AS max_date 
          FROM table_3 
          WHERE table_3.rec_id = table_4.rec_id 
           AND table_3.note_date <= table_4.report_date)) t3 
       ON t3.rec_id = table_4.rec_id 
WHERE table_4.site_id = '1'; 

@shawno:你說的沒錯,在with條款是有缺陷的,因爲我誤解你的初始查詢。以上是一個更正的版本。由於max值是特定於每行的,因此您已經用於獲取這些值的方法可能是最有效的。對於優化這個最佳選擇似乎只是將子查詢從select子句移動到from子句。

此外,這是一個未經測試的解決方案,因爲我既沒有您的表格結構也沒有您的數據。在不付出太多努力的情況下,我可以做的最好的事情是驗證語法是否有效。

+0

對於SQL-99語法爲+1。恕我直言,任何人都不應該繼續使用舊的SQL-86語法。 – Ollie

+0

@Oliie - 仍然有選擇舊語法的合理原因,而不是ANSI語法。作爲一個例子,請查看喬納森劉易斯的詳細博客文章:http://jonathanlewis.wordpress.com/2010/12/03/ansi-argh/ – APC

+0

感謝您的幫助。但是,我不明白你的解決方案是如何工作的 - 你不應該在with子句中引用table_4嗎?如果我不那麼我會得到一個ORA-00904錯誤 - 無效的標識符。此外,它似乎只會產生table_3.note_date中所有日期的最大日期(即只返回一個日期)。我需要表4中每個rec_id的最大日期,它等於table_3中的rec_id(其中有許多相同的rec_id,但我只想匹配具有最大日期的rec_id)。這就是爲什麼有嵌套的相關子查詢來查找最大日期。有任何想法嗎? – shawno