2

ここに私の質問があります、

   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 のレコードがあります。

ID、ID と STARTDTE、STARTDTE、ID、ID2 と STARTDTE の TABLE2 に個別のインデックスがあります。

クエリはまだ遅すぎます。これはどのように改善できますか?助けてください。

4

3 に答える 3

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)。インデックスの順序は非常に重要です。

于 2012-09-25T14:55:59.533 に答える
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)
于 2012-09-25T14:43:41.737 に答える
1

2 番目のテーブルに 2 回アクセスする必要がないように、分析関数を調べることができます。このようなものがうまくいくかもしれません:

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;

内部選択は、orに対する単純な結合を使用して、2 つのテーブルからidおよびの値を取得します。この関数は、に基づいて、2 番目のテーブルで一致する各行のランキングを計算します。その日付でフィルタリングするのは少し複雑なので、評価された値を null に変更することで、今日以降の日付を効果的に無視するためにa を使用しました。無視されます。vididid2rankstartdtecaseorder byovernulls last

最初に内部選択を単独で実行することをお勧めします-おそらくid簡潔にするためにいくつかの値を使用して-それが何をしているか、どのランクが割り当てられているかを確認します。

外側のクエリは、各 のトップランクの結果を選択するだけidです。

ただし、重複する可能性があります。table2複数の行がid同じである場合startdte、それらは同じランクになりますが、以前にそのような状況があった可能性があります。order by意味のある方法で関係を断ち切るために、 にさらにフィールドを追加する必要がある場合があります。

しかし、これは主に憶測であり、既存のクエリが実際に遅い場所を確認することはできません。

于 2012-09-25T14:59:45.277 に答える