1

わかりましたので、私は職場でこの (かなり古い) プロジェクトに取り組んでおり、Oracle データベースに対して大量のクエリを使用しています。私は最近、実行に約 6 ~ 7時間かかり、約1400 行を返すこの宝石に出くわしました。問題のテーブル/ビューには ~200'000 行が含まれています。合理的と思われるよりも少し時間がかかっているように感じたので、詳しく調べてみました。セキュリティ/独自の理由から、正確なクエリを共有することはできませんが、これはクエリがより一般的な用語で何をするかを示すはずです:

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 (顧客/エンゲージメントの評価を含む) から、一意のクライアント/エンゲージメントごとに最新の評価を取得することになっています。

2 つの結合は、クライアントとエンゲージメントが別のシステムに存在することを確認するためにあります。このシステムで評価を行った後、それらは主に処理されます。

2 つの数値と日付を連結し、それをサブクエリと比較する方法に注目してください。「面白い」デザインチョイス。したがって、連結を適切な比較に置き換えると、少なくとも何らかのパフォーマンスが向上する可能性があると考えました。私は主に .NET と Web の開発を行っており、データベースに関しては専門家ではありませんが、次のように書き直しました。

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 時間近くかかります。現在実行中なので、新しいクエリが有効かどうかを確認するために何らかの分析を行うことができますが、明らかに間違っていることがあるかどうかを確認するために、ここでも質問したいと思いました。

編集また、最も外側のクエリを削除しました。これは、クエリをよりクールに見せることを除いて、何らかの目的を果たしているようには見えませんでした..または何か..私は知りません..

4

1 に答える 1

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
    );

代わりに分析関数を使用して、自己結合を回避するためにそれを書き直した場合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_objectsall_tablesmax

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

どちらもtrunc同じ日にすべてを取得するために使用しています。registered_date時間コンポーネントがない場合は必要ないかもしれません。

もちろん、実際に同じ結果が得られるかどうかを確認してください。

于 2012-10-19T12:27:56.780 に答える