1

ケースを単純化するために、次の 3 つのテーブルがあるとします。

A(a_id)、B(b_id、val_b)、C(a_id、b_id、val_c)

B と C から特定の値のペアを持つすべての a_id を見つける必要があります。 ' および val_c='4' および B.b_id=C.b_id) AND ...

select A.a_id
from A
where (A.a_id in 
        (select C.a_id 
         from B, C 
         where B.b_id=C.b_id and B.val_b='1' and C.val_c='2') and
       A.a_id in 
        (select C.a_id 
         from B, C 
         where B.b_id=C.b_id and B.val_b='3' and C.val_c='4') and
       A.a_id in 
        (select C.a_id 
         from B, C 
         where B.b_id=C.b_id and B.val_b='5' and C.val_c='6'));

私が気付いたのは、(val_b,val_c) ペアをさらにいくつか追加すると、postgres がクエリを実行するのにかなりの時間がかかることです。ID、val_b および val_c のインデックスが存在することに注意してください。

クエリを最適化する方法はありますか? 明示的な内部結合を試みましたが、パフォーマンスの向上には役立ちませんでした。

前もって感謝します

より詳しい情報:

  • postgres バージョン 8.2.4
  • ペア基準が 1 つだけの場合、77.621 ミリ秒で実行されます
  • 2 ペア基準の場合 - 151.588 ミリ秒
  • 3 組の基準 - 49483.979 ミリ秒 <-- パフォーマンスが狂ったように火花を散らす

  • 別のサブクエリ自体は最大 62 ミリ秒で実行されることに注意してください。

アップデート:

Vladimir Baranov によって以下に提案された個別の INTERSECT クエリ バージョンと、Clodoaldo Neto によって bool_or 集計関数を使用する has 句を含むバージョンの両方が、はるかに優れたパフォーマンスを発揮しました。ありがとうございました !

ただし、postgres 8.2 が 3 つのペア基準で始まる元のクエリでパフォーマンスが向上する理由は何ですか?

ところで、Vladimir Baranov の最初の提案で、クエリをクリーン ジョインで書き直すという同じ火花に気付きました。下記参照:

SELECT A.a_id
FROM
    A
    INNER JOIN (SELECT C.a_id FROM B INNER JOIN C ON B.b_id=C.b_id WHERE B.val_b='1' and C.val_c='2') Set1 ON Set1.a_id = A.a_id
    INNER JOIN (SELECT C.a_id FROM B INNER JOIN C ON B.b_id=C.b_id WHERE B.val_b='3' and C.val_c='4') Set2 ON Set2.a_id = A.a_id
    INNER JOIN (SELECT C.a_id FROM B INNER JOIN C ON B.b_id=C.b_id WHERE B.val_b='5' and C.val_c='6') Set3 ON Set3.a_id = A.a_id
;

3 セットのクエリは非常に高速に実行されますが、別の 3 ~ 4 セットを追加するとすぐに、クエリのパフォーマンスが 30 ~ 40 秒に低下します。

4

4 に答える 4

1

以下がより速く実行されるかどうかを確認することは興味深いでしょう:

SELECT A.a_id
FROM A
WHERE
    A.a_id IN
    (
        SELECT C.a_id
        FROM B INNER JOIN C ON B.b_id=C.b_id
        WHERE B.val_b='1' and C.val_c='2'

        INTERSECT

        SELECT C.a_id
        FROM B INNER JOIN C ON B.b_id=C.b_id
        WHERE B.val_b='3' and C.val_c='4'

        INTERSECT

        SELECT C.a_id
        FROM B INNER JOIN C ON B.b_id=C.b_id
        WHERE B.val_b='5' and C.val_c='6'
    )
;

事実上、INここでは複数の代わりに、複数のサブセットの明示的な交差があります。

私の元の回答には、質問の元のクエリと同等ではないクエリがありました。

これは、私のバリアントが元のクエリと同じ結果を生成することを確認するために、いくつかのサンプル データと元のクエリを含むSQL Fiddleです。

編集

調査するもう 1 つのパス。各サブクエリの実行は速いがINTERSECT、1 つの長いクエリで何度も繰り返すと非常に遅くなる場合は、一時テーブルにサブクエリの結果を入力してから、この一時テーブルをメイン テーブルで使用することができますAINTERSECT効果的には、明示的な一時テーブルを使用して、一度に 1 セットずつ手動で実装します。サブクエリによって返される行数によっては、一時テーブルにインデックスを追加することが有益な場合があります。

アップデート

クエリが複雑になると Postgres のパフォーマンスが低下する理由についての質問については、Postgres のバージョンがかなり古く、詳細に調査するほど興味がある人はいないでしょう。私はいくつかの一般的な考えしか提供できません。8.2 から多くの変更が加えられたため、最新バージョンではパフォーマンスが異なる可能性が高いです。

すべての RDBMS で、クエリ オプティマイザーがクエリを分析するためのリソースと時間は限られているため、多くのヒューリスティックを使用します。クエリ内の結合の数が増えるにつれて、最適な実行計画を見つけるための問題の複雑さが指数関数的に増加するため、オプティマイザがあきらめて、彼が持っている計画を選択するしきい値が必要です。

あなたはそれを観察できるはずです。高速クエリの実行プランを調べ、別の結合を追加してクエリを遅くし、プランを比較します。おそらく、計画は大きく異なるでしょう。それぞれの場合にオプティマイザが選択するパスを決定できるはずです。

オプティマイザーがほとんどないクエリが与えられた場合、joinsそれを を使用するのと同等のバリアントに変換できますintersectが、結合が多数ある場合はそれ以上実行できず、結合ごとに結合を行うクエリフローに従っている可能性があります。ループ内のループ内でループを実行することになるほど非効率的に実行することさえあります...つまり、複雑さは線形から二次またはさらに悪化します。

したがって、実際には、このようなパフォーマンスに関する質問に対する唯一の答えは、実行計画を調べることです。

ところで、Postgres の最新バージョンには がありWITH、中間結果を含む一時テーブルを効果的に作成します。各サブクエリは単純であり、システムが最初にそれらすべてを個別に実行すると、結果を簡単に組み合わせることができるため、これはあなたの場合に大いに役立つはずです。

于 2015-02-24T01:54:01.250 に答える
1
select a_id
from
    a
    inner join
    c using (a_id)
    inner join
    b using (b_id)
group by a_id
having
    bool_or((val_b, val_c) = (1,2)) and
    bool_or((val_b, val_c) = (3,4)) and
    bool_or((val_b, val_c) = (5,6))

http://www.postgresql.org/docs/8.2/static/functions-aggregate.html

于 2015-02-24T12:56:00.097 に答える
0
  1. 最新バージョンにアップグレードする
  2. JOINわかりやすくするために構文を使用します
  3. 速度と快適さEXISTS(...)の代わりに使用IN(...)
  4. PK/FK とインデックスが役に立ちます!

SELECT A.a_id
FROM A
WHERE EXISTS (
        SELECT *
        FROM B
        JOIN C ON B.b_id = C.b_id AND B.val_b = '1' 
        WHERE C.a_id = A.a_id AND C.val_c = '2'
        )
AND EXISTS (
        SELECT *
        FROM B
        JOIN C ON B.b_id = C.b_id AND B.val_b = '3' 
        WHERE C.a_id = A.a_id AND C.val_c = '4'
        )
AND EXISTS (
        SELECT *
        FROM B
        JOIN C ON B.b_id = C.b_id AND B.val_b = '5' 
        WHERE C.a_id = A.a_id AND C.val_c = '6'
        )
        ;
于 2015-02-24T12:22:41.880 に答える