次の2つのクエリを検討してください。
_SELECT
t1.id, *
FROM
t1
INNER JOIN
t2 ON t1.id = t2.id
where t1.id > -9223372036513411363;
_
そして:
_SELECT
t1.id, *
FROM
t1
INNER JOIN
t2 ON t1.id = t2.id
where t1.id > -9223372036513411363 and t2.id > -9223372036513411363;
_
注:_-9223372036513411363
_はテーブルの最小値ではなく、条件により結果が減少します(行の総数から、3億5000万)1700万に。
個人的には、_t1.id = t2.id
_が自動的に2番目の条件を意味するため、PostgreSQLは両方のクエリに対して同じ計画を立てることを期待しています。しかし残念ながら、PostgreSQLは2つの異なる計画を作成しており、2番目の計画はより優れています。
結合からビューを作成し、ビューのクエリにwhere条件を配置したいので、最初のクエリを強く希望します。そこでは、単一のid列が表示されます(USING
を使用して結合するため、単一のid列が表示されます)。また、3つ以上のテーブルを結合するので、結合ごとにこのような条件を追加しない方がよいでしょう。
この動作には何らかの理由がありますか?それともバグですか?回避策はありますか?
ON t1.id = t2.id
_をUSING (id)
で置き換えても、両方のクエリで違いはありません。id
列は、両方のテーブルの主キーです。テーブルには約350,000,000行あります。 t1は約20GiB、t2は14GiBです。INNER JOIN
_を_LEFT OUTER JOIN
_で置き換えると、同様の結果が生成されます行の多いDBがあり、新しいデータが継続的に挿入されています。このデータには、さまざまな種類のクエリ(さまざまなデータの検索、各列による並べ替え、集計クエリなど)を含むさまざまなレポートを生成する必要があります。
現在の設計では、UPDATE操作はありません。現在、高度に正規化されたデザイン(アンカーモデリングや6NFによって促進されたアイデアに基づく)を実験しています。そのような設計では、JOINとVIEWを広範囲に使用してDBでの作業を快適にするため、これらを効率的に実行できるデータベースが必要です。
(このような問題に基づいて)私が知る限り、PostgreSQLはこのデザイン(約11のテーブルと多数のビュー)に適しているようには見えず、ほとんど常に正規化されていないデザインよりもパフォーマンスが悪いようです1つまたは2つのテーブルがあり、ビューはありません。 JOINクエリを計画する際のこの問題が私のせいだと思っていましたが、まだそうではありません。この問題では、VIEWSを使用することを忘れて、繰り返しの多い条件で詳細なクエリを使用するか、PostgreSQLまたはこのデザインを使用することを忘れるべきです。
実際の列数はもう少し多くなりますが、他のテーブルとは関係がないため、この説明とは無関係です。
_CREATE TABLE t1
(
id bigint NOT NULL DEFAULT nextval('ids_seq'::regclass),
total integer NOT NULL,
price integer NOT NULL,
CONSTRAINT pk_t1 PRIMARY KEY (id)
)
CREATE TABLE t2
(
id bigint NOT NULL,
category smallint NOT NULL,
CONSTRAINT pk_t2 PRIMARY KEY (id),
CONSTRAINT fk_id FOREIGN KEY (id)
REFERENCES t1 (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
_
次に、それはオプティマイザの死角のように見え、2番目のクエリを使用する必要があります。
2つのテーブルa
とb
を結合する条件がある場合:_a.id = b.id
_と追加の条件_a.id > @some_constant
_は、オプティマイザが「インデックス条件」を使用してa (id)
インデックスでインデックススキャンを開始しますが、2番目のインデックスb (id)
には使用しません。
したがって、(冗長な)_b.id > @some_constant
_を追加すると、b (id)
インデックスの一部もスキップして、わずかに効率的なプランを作成できます。
これは、Postgresハッカーグループに改善の提案として投稿することができます(まだ行っていない場合)。
編集後、_FOREIGN KEY
_から_t2
_の_REFERENCES t1
_制約があることがわかります。したがって、クエリを記述する「自然な」(同等の)方法は次のようになります。
_SELECT
-- whatever
FROM
t2
LEFT JOIN
t1 ON t1.id = t2.id
WHERE t2.id > -9223372036513411363 ;
_
これを試して、作成した実行計画を教えてください。 LEFT
(外部)結合にのみ適用され、内部結合には適用されないいくつかの変換があります。
残念ながら、これは別の計画も作成しません。
OPがPostgresパフォーマンスリストに質問を投稿しました。ここでスレッド全体を見ることができます: PostgreSQLは、単純な条件付き結合 で非効率的な計画を作成しているようです。これは考慮された機能ですが、オプティマイザにはまだ実装されていない機能であることを確認します。
はい、残念ながら、実行できることは1つだけです。それは、クエリに両方の条件を含めるだけです。特別な理由がありますか?クエリに_
t2.id > ...
_条件を書き込むこともできませんか?または、クエリを制御できないソフトウェアによって動的に生成されていますか?私は個人的にはこの分野での改善を見たいと思っていますし、この問題を修正するパッチ 1 も書いています。この修正を提案するときに私が抱えていた問題は、このプランナーの制限によって影響を受ける人々の数に関する詳細を報告できないことでした。私が提案したパッチは、多くのクエリの計画時間に非常に小さな影響を与え、多くの人が十分な場合に適用しないと、メリットが得られないクエリをスローダウンする価値があると考えられていました。もちろん私はこれに同意します。クエリの計画を遅らせることに関心はありませんが、同時に、この領域での厄介な最適化の悪さを理解しています。
ただし、私が提案したパッチは最初のドラフト案にすぎないことを覚えておいてください。本番用ではありません。