在不使用相关子查询的情况下重写查询

时间:2011-11-21 20:54:26

标签: oracle performance correlated-subquery

在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                 

所以我真的很难过。有任何想法吗? - 谢谢。

1 个答案:

答案 0 :(得分: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子句。

此外,这是一个未经测试的解决方案,因为我既没有您的表结构也没有您的数据。我能做的最好的事情就是验证语法是否有效。