如何使用子查询Oracle优化此SELECT

时间:2012-09-25 14:21:37

标签: oracle indexing

这是我的查询,

   SELECT ID As Col1,
   (
         SELECT VID FROM TABLE2 t 
         WHERE (a.ID=t.ID or a.ID=t.ID2) 
         AND t.STARTDTE =
         (
               SELECT MAX(tt.STARTDTE) 
               FROM TABLE2 tt 
               WHERE (a.ID=tt.ID or a.ID=tt.ID2)  AND tt.STARTDTE < SYSDATE
         )
   ) As Col2
   FROM TABLE1 a

Table1有48850条记录,Table2有15944098条记录。

我在TABLE2中有关于ID,ID和amp;的单独索引。 STARTDTE,STARTDTE,ID,ID2&amp; STARTDTE。

查询仍然太慢。如何改进?请帮忙。

3 个答案:

答案 0 :(得分:3)

我猜测内部查询中的OR搞乱了优化器使用索引的能力。此外,我不建议使用扫描所有TABLE2给定其大小的解决方案。

这就是为什么在这种情况下我会建议使用一个能够有效检索您正在寻找的信息的函数(每次调用2个索引扫描):

CREATE OR REPLACE FUNCTION getvid(p_id table1.id%TYPE) 
   RETURN table2.vid%TYPE IS
   l_result table2.vid%TYPE;
BEGIN
   SELECT vid
     INTO l_result
     FROM (SELECT vid, startdte
             FROM (SELECT vid, startdte
                     FROM table2 t
                    WHERE t.id = p_id
                      AND t.startdte < SYSDATE
                    ORDER BY t.startdte DESC)
            WHERE rownum = 1
           UNION ALL
           SELECT vid, startdte
             FROM (SELECT vid, startdte
                     FROM table2 t
                    WHERE t.id2 = p_id
                      AND t.startdte < SYSDATE
                    ORDER BY t.startdte DESC)
            WHERE rownum = 1
            ORDER BY startdte DESC)
    WHERE rownum = 1;
   RETURN l_result;
END;

您的SQL将成为:

SELECT ID As Col1,
       getvid(a.id) vid
  FROM TABLE1 a

确保table2(id, startdte DESC)table2(id2, startdte DESC)都有索引。索引的顺序非常重要。

答案 1 :(得分:1)

可能会尝试以下方法,但未经测试。

WITH max_times AS
    (SELECT a.ID, MAX(t.STARTDTE) AS Startdte
     FROM TABLE1 a, TABLE2 t 
     WHERE (a.ID=t.ID OR a.ID=t.ID2) 
         AND t.STARTDTE < SYSDATE
     GROUP BY a.ID)
SELECT b.ID As Col1, tt.VID
FROM TABLE1 b
    LEFT OUTER JOIN max_times mt
    ON (b.ID = mt.ID)
    LEFT OUTER JOIN TABLE2 tt
    ON ((mt.ID=tt.ID OR mt.ID=tt.ID2) 
        AND mt.startdte = tt.startdte)

答案 2 :(得分:1)

您可以查看analytic functions以避免必须两次击中第二张桌子。这样的事情可能有用:

SELECT id AS col1, vid
FROM (
    SELECT t1.id, t2.vid, RANK() OVER (PARTITION BY t1.id ORDER BY
        CASE WHEN t2.startdte < TRUNC(SYSDATE) THEN t2.startdte ELSE null END
        NULLS LAST) AS rn
    FROM table1 t1
    JOIN table2 t2 ON t2.id IN (t1.ID, t1.ID2)
)
WHERE rn = 1;

内部选择通过idvid上的简单联接从两个表中获取idid2值。 rank函数根据startdte计算第二个表中每个匹配行的排名。你希望在那个日期过滤它有点复杂,所以我使用case通过将评估值更改为null来有效地忽略今天或稍后的任何日期,在这种情况下,这意味着{{1 <{1}}子句需要order by,因此忽略它们。

我建议你先自己运行内部选择 - 为简洁起见,可能只需要几个over值 - 看看它做了什么,以及分配了什么等级。

外部查询只是为每个nulls last选择排名靠前的结果。

你可能仍会得到重复;如果id对于具有相同id的{​​{1}}有多行,则他们将获得相同的排名,但之后您可能已经遇到过这种情况。您可能需要向table2添加更多字段,以便以对您有所帮助的方式打破关系。

但这很大程度上是在猜测你现有的查询实际上很慢的原因。