web-dev-qa-db-ja.com

PostgreSQLのインデックスをカバーすることは、列を結合するのに役立ちますか?

私は漠然と次のように見えるテーブルがたくさんあります:

CREATE TABLE table1(id INTEGER PRIMARY KEY, t1c1 INTEGER, t1c2 INTEGER);
CREATE TABLE table2(id INTEGER PRIMARY KEY, t1 INTEGER REFERENCES table1(id), t2c1 INTEGER);

そして、次のように、最初のテーブルからデータを取得するために、結合されたテーブルでフィルタリングしようとしている結合の多くを実行します。

SELECT t1c1
FROM table1
JOIN table2 ON table2.t1 = table1.id
WHERE t2c1 = 42;

テーブルのインデックスを作成するときは、WHERE句で使用される列を確認し、それらを満足するインデックスを作成します。したがって、このクエリでは、次のようなインデックスを作成します。

CREATE INDEX ON table2 (t2c1);

そして、このインデックスは少なくともそのクエリでの使用に適しています。

私の質問は、私がこのようなインデックスを書いた場合です:

CREATE INDEX ON table2 (t2c1, t1);

インデックスは、上記のクエリのJOINに役立つカバリングインデックスとして使用されますか?外部キー列をカバーするようにインデックス書き込み戦略を変更する必要がありますか?

8
ldrg

インデックスは、上記のクエリのJOINに役立つカバリングインデックスとして使用されますか?

場合によります。 Postgresには "index-only" scans as index access methodがあります。 、それ自体「カバーインデックス」はありません-Postgres 10まで。

Postgres 11から始まり、INCLUDE列を含む真のカバーするインデックス が利用可能です。 Michael Paquierによる機能を紹介するブログエントリ:

コード例と関連する回答:

そうは言っても、インデックスCREATE INDEX ON table2 (t2c1, t1);は、実証するクエリにとって完全に理にかなっています。追加の前提条件が満たされている場合は、インデックスのみのスキャンに使用できます。または、ビットマップインデックススキャンまたはプレーンインデックススキャンで使用できます。関連:

JOIN条件とWHERE条件は、Postgresではほとんど完全に同等です。確かに同じ方法でインデックスを使用できます。クエリを書き直すことができます

SELECT t1c1
FROM   table1
JOIN   table2 ON table2.t1 = table1.id
WHERE  t2c1 = 42;

これと同等のもの:

SELECT t1c1
FROM   table1 CROSS JOIN table2
WHERE  table2.t1 = table1.id
AND    table2.t2c1 = 42;

ただし、最初の形式が明らかに望ましいです。読みやすい。

なぜ「ほぼ」同等なのですか?

15

インデックスは、上記のクエリのJOINに役立つカバリングインデックスとして使用されますか?外部キー列をカバーするようにインデックス書き込み戦略を変更する必要がありますか?

上記のクエリではありません。これは、2つの条件の推定と選択性に基づく結果を伴う、複雑な問題です。

  • table2.t1 = table1.id
  • t2c1 = 42

基本的に、両方の環境の選択性を高めるために、両方の環境(行数)をスローする必要があります。そして、ネストされたループが発生した場合、それが最も実行可能な結合方法でなくなるまで、生の量を増やしたいと思います。

CREATE TABLE table1(
   id INTEGER PRIMARY KEY,
   t1c1 INTEGER,
   t1c2 INTEGER
);
INSERT INTO table1(id, t1c1, t1c2)
  SELECT x,x,x FROM generate_series(1,1000)
  AS gs(x);

CREATE TABLE table2(
  id INTEGER PRIMARY KEY,
  t1 INTEGER REFERENCES table1(id),
  t2c1 INTEGER
);
INSERT INTO table2(id, t1, t2c1)
SELECT x,1+x%1000,x%50 FROM generate_series(1,1e6)
  AS gs(x);

EXPLAIN ANALYZE
  SELECT t1c1
  FROM table1
  JOIN table2 ON table2.t1 = table1.id
  WHERE t2c1 = 42;

計画を確認してください。

次に、複合インデックスを作成します。

CREATE INDEX ON table2 (t2c1, t1);
VACUUM FULL ANALYZE table1;
VACUUM FULL ANALYZE table2;

もう一度計画を確認してください

EXPLAIN ANALYZE
  SELECT t1c1
  FROM table1
  JOIN table2 ON table2.t1 = table1.id
  WHERE t2c1 = 42;

キーなどをドロップして、好みの形式を見つけることができます

CREATE INDEX ON table2 (t1, t2c1);

または

CREATE INDEX ON table2 (t2c1, t1);

結局これは大変な作業ですが、

CREATE INDEX ON table2 (t1);
CREATE INDEX ON table2 (t2c1);

そして、それが十分でない場合にのみ最適化します。

特定のプランナーオプションを無効にして、別のプランreallyの方が速いか遅いかを確認し、それを修正することを検討することもできますが、それも多くの作業になる可能性があります。

3
Evan Carroll