LATERALとPostgreSQLのサブクエリの違いは何ですか?


146

PostgresがLATERAL結合を行う機能を備えて以来、私は現在、全体のクエリに4分以上かかる多くの非効率的なサブクエリを使用してチームの複雑なデータダンプを行っているので、それについて調べてきました。

LATERAL結合が役立つ場合があることを理解していますが、Heap Analyticsからこのような記事を読んだ後でも、私はまだ完全には従いません。

LATERAL結合のユースケースは何ですか?LATERAL結合とサブクエリの違いは何ですか?


2
blog.heapanalytics.com/...explainextended.com/2009/07/16/inner-join-vs-cross-apply(SQL Serverのはapply同じであるlateralSQL標準から)
a_horse_with_no_name

回答:


163

相関サブクエリに似ている

LATERAL(Postgresの9.3またはそれ以降)は、より似ている参加相関サブクエリではなく、普通サブクエリ。Andomarが指摘したように、LATERAL結合の右側の関数またはサブクエリは、相関サブクエリと同様に、その左側の各行に対して1回評価する必要がありますが、プレーンサブクエリ(テーブル式)は1回だけ評価されます。(ただし、クエリプランナーには、どちらか一方のパフォーマンスを最適化する方法があります。)
この関連する回答には、同じ問題を解決する両方のコード例があります。

複数の列を返す場合LATERAL結合は通常、よりシンプルでクリーンで高速です。
また、相関サブクエリに相当するものはLEFT JOIN LATERAL ... ON true次のとおりです。

マニュアルを読む LATERAL

これは、ここで回答に入れるものよりも信頼性があります。

サブクエリで実行できないこと

ありますことを、物事LATERALを行うことができます参加するには、しかし、(相関)サブクエリは(簡単に)することはできません。相関サブクエリは、複数の列や複数の行ではなく、単一の値のみを返すことができます。ただし、裸の関数呼び出し(複数の行を返す場合に結果行を乗算する)を除きます。ただし、特定のセットを返す関数でさえ、FROM句でのみ許可されます。unnest()Postgres 9.4以降の複数のパラメータと同様です。マニュアル:

これはFROM句でのみ許可されます。

したがって、これは機能しますが、サブクエリで簡単に置き換えることはできません。

CREATE TABLE tbl (a1 int[], a2 int[]);
SELECT * FROM tbl, unnest(a1, a2) u(elem1, elem2);  -- implicit LATERAL

句のコンマ(,)は、のFROM短い表記ですCROSS JOIN
LATERALテーブル関数では自動的に想定されます。
の特殊なケースの詳細UNNEST( array_expression [, ... ] )

SELECTリスト内のセットを返す関数

リストのようunnest()SELECT直接セットを返す関数を使用することもできます。これは、SELECTPostgres 9.6まで、同じリストにこのような関数が複数あるという驚くべき動作を示していました。しかし、それは最終的にPostgres 10でサニタイズされ、現在では有効な代替手段です(標準SQLでなくても)。見る:

上記の例に基づいて構築:

SELECT *, unnest(a1) AS elem1, unnest(a2) AS elem2
FROM   tbl;

比較:

PG 9.6用dbfiddle ここでは、
PG 10用dbfiddle こちら

誤った情報を明確にする

マニュアル:

以下のためにINNEROUTER参加の種類、状態、すなわち正確の一つに指定されている必要があり参加しNATURALON join_condition、もしくはUSINGjoin_columnを [、...])。意味については以下を参照してください。
の場合CROSS JOIN、これらの句は表示されません。

したがって、次の2つのクエリは有効です(特に有用ではない場合でも)。

SELECT *
FROM   tbl t
LEFT   JOIN LATERAL (SELECT * FROM b WHERE b.t_id = t.t_id) t ON TRUE;

SELECT *
FROM   tbl t, LATERAL (SELECT * FROM b WHERE b.t_id = t.t_id) t;

これはそうではありませんが:

SELECT *
FROM   tbl t
LEFT   JOIN LATERAL (SELECT * FROM b WHERE b.t_id = t.t_id) t;

だからこそだAndomarの@コード例正しい(CROSS JOIN結合条件を必要としない)とアッティラの@ IS無効でした。


サブクエリがLATERAL JOINで実行できないことがいくつかあります。ウィンドウ関数のように。このまま
エヴァンキャロル

@EvanCarroll:リンクに関連するサブクエリが見つかりませんでした。しかし、LATERALサブクエリでウィンドウ関数を示す別の回答を追加しました:gis.stackexchange.com/a/230070/7244
Erwin Brandstetter 2017

1
よりクリーンで高速ですか?マグニチュードが速くなる場合もあります。LATERALに切り替えてから数日から数秒でクエリがありました。
rovyko

51

非結合laterallateral結合の違いは、左側のテーブルの行を見ることができるかどうかにあります。例えば:

select  *
from    table1 t1
cross join lateral
        (
        select  *
        from    t2
        where   t1.col1 = t2.col1 -- Only allowed because of lateral
        ) sub

この「外向き」は、サブクエリを複数回評価する必要があることを意味します。結局のところ、t1.col1多くの値を想定できます。

対照的に、非lateral結合後のサブクエリは1回だけ評価できます。

select  *
from    table1 t1
cross join
        (
        select  *
        from    t2
        where   t2.col1 = 42 -- No reference to outer query
        ) sub

なしlateralで必要とされるように、内部クエリは外部クエリにまったく依存しません。lateralクエリは、一例であるcorrelatedため、クエリ自体の外側行との関係を、クエリ。


5
これがラテラルジョインの最もわかりやすい説明です。
1valdis

わかりやすい説明、ありがとうございます。
arilwan

どのようにselect * from table1 left join t2 using (col1)比較しますか?/ on条件を使用した結合が不十分であるかどうかは不明であり、ラテラルを使用する方が理にかなっています。
No_name

9

まず、ラテラルとクロス適用は同じものです。したがって、クロスアプライについてもお読みください。これはSQL Serverに古くから実装されていたため、ラテラルよりもさらに詳しい情報が得られます。

第二に、私の理解よれば、ラテラルを使用する代わりにサブクエリを使用してできないことはありません。だが:

次のクエリを検討してください。

Select A.*
, (Select B.Column1 from B where B.Fk1 = A.PK and Limit 1)
, (Select B.Column2 from B where B.Fk1 = A.PK and Limit 1)
FROM A 

この状態でラテラルを使用できます。

Select A.*
, x.Column1
, x.Column2
FROM A LEFT JOIN LATERAL (
  Select B.Column1,B.Column2,B.Fk1 from B  Limit 1
) x ON X.Fk1 = A.PK

このクエリでは、limit句のため、通常の結合を使用できません。単純な結合条件がない場合は、ラテラルまたはクロスアプライを使用できます。

ラテラルまたはクロスアプライの使用法は他にもありますが、これは私が見つけた最も一般的なものです。


1
正確には、なぜPostgreSQLがのlateral代わりに使用するのかと思いますapply。おそらく、Microsoftは構文の特許を取得していますか?
Andomar 2015

9
@Andomar AFAIK lateralはSQL標準に含まれていますが、そうでapplyはありません。
muが短すぎる

LEFT JOINは結合条件が必要です。それを作るON TRUEあなたが何らかの形で制限したい場合を除きます。
Erwin Brandstetter、2015

アーウィンは正しい、cross joinまたはon条件を使用しない限り、エラーが発生します
Andomar

1
@Andomar:この誤った情報に拍車をかけた私は明確にするために別の答えを追加しました。
Erwin Brandstetter、2015

4

誰も指摘していないことの1つはLATERAL、クエリを使用して、選択したすべての行にユーザー定義関数を適用できることです。

例えば:

CREATE OR REPLACE FUNCTION delete_company(companyId varchar(255))
RETURNS void AS $$
    BEGIN
        DELETE FROM company_settings WHERE "company_id"=company_id;
        DELETE FROM users WHERE "company_id"=companyId;
        DELETE FROM companies WHERE id=companyId;
    END; 
$$ LANGUAGE plpgsql;

SELECT * FROM (
    SELECT id, name, created_at FROM companies WHERE created_at < '2018-01-01'
) c, LATERAL delete_company(c.id);

これが、PostgreSQLでこの種のことを行う方法を知る唯一の方法です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.