2

Postgres 9.3 を使用しています。

私は2つのテーブルT1 とそれらの間T2n:m関係を持ってT1_T2_relいます。ここで、T1 の列に加えて、T1 の各レコードに対して、T2 のすべての関連レコードの主キー ID を持つ配列を含む列を提供するビューを作成したいと思います。T2 に関連するエントリがない場合、この列の対応するフィールドには null 値が含まれます。

私のスキーマの抽象化されたバージョンは次のようになります。

CREATE TABLE T1 ( t1_id serial primary key, t1_data int );

CREATE TABLE T2 ( t2_id serial primary key );

CREATE TABLE T1_T2_rel (
  t1_id int references T1( t1_id )
, t2_id int references T2( t2_id )
);

対応するサンプル データは、次のように生成できます。

INSERT INTO T1 (t1_data)
SELECT cast(random()*100 as int) FROM generate_series(0,9) c(i);

INSERT INTO T2 (t2_id) SELECT nextval('T2_t2_id_seq') FROM generate_series(0,99);

INSERT INTO T1_T2_rel
SELECT cast(random()*10 as int) % 10 + 1 as t1_id
     , cast(random()*99+1 as int) as t2_id
FROM   generate_series(0,99);

これまでのところ、次のクエリを思いつきました。

SELECT T1.t1_id, T1.t1_data, agg
FROM T1
LEFT JOIN LATERAL (
   SELECT t1_id, array_agg(t2_id) as agg
   FROM T1_T2_rel
   WHERE t1_id=T1.t1_id
   GROUP BY t1_id
   ) as temp ON temp.t1_id=T1.t1_id;

これは機能します。しかし、それは単純化できますか?

対応するフィドルは次の場所にあります: sql-fiddle。残念ながら、sql-fiddle は横結合に必要な Postgres 9.3 を (まだ) サポートしていません。

追記】 ご指摘の通りleft join、基本的にはサブクエリを使ったシンプルなもので十分です。ただし、クエリ プランを比較すると、Postgres は を使用する場合は集計テーブルの順次スキャンに頼りleft joinますが、 の場合はインデックス スキャンが使用されますleft join lateral

4

1 に答える 1

8

@Denis がすでにコメントしているように、. は必要ありませんLATERAL。また、サブクエリが間違った列を選択しました。これは機能します:

SELECT t1.t1_id, t1.t1_data, t2_ids
FROM   t1
LEFT   JOIN (
    SELECT t1_id, array_agg(t2_id) AS t2_ids
    FROM   t1_t2_rel
    GROUP  BY 1
    ) sub USING (t1_id);

-SQL フィドル。

パフォーマンスとテスト

あなたが言及したその後のシーケンシャルスキャンに関して:テーブル全体をクエリすると、シーケンシャルスキャンの方が多くの場合高速です。実行しているバージョン、ハードウェア、カーディナリティの設定と統計、およびデータの分布によって異なります。orのWHEREような選択句を試して、プランナーの設定と組み合わせて、選択肢について学習します。WHERE t1.t1_id < 1000WHERE t1.t1_id = 1000

SET enable_seqscan = off;
SET enable_indexscan = off;

リセットするには:

RESET enable_seqscan;
RESET enable_indexscan;

あなたのローカルセッションでのみ、気をつけてください!dba.SE に関するこの関連する回答には、さらに詳しい説明があります。
もちろん、設定もオフになっている可能性があります。

于 2013-10-21T20:13:56.203 に答える