web-dev-qa-db-ja.com

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

PostgresがLATERAL結合を実行できるようになったので、現在、チーム全体でクエリが4分以上かかる非効率なサブクエリを多数使用して複雑なデータダンプを実行しているので、読み進めています。

LATERAL結合が私を助けてくれるかもしれないことを理解していますが、Heap Analyticsの this one のような記事を読んだ後でも、私はまだ十分には従いません。

LATERAL結合の使用例は何ですか? LATERAL結合とサブクエリの違いは何ですか?

101
jdotjdot

correlatedサブクエリのようなもの

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

複数の列を返すの場合、通常、LATERAL結合はより単純で、よりクリーンで、高速です。
また、相関サブクエリに相当するものはLEFT JOIN LATERAL ... ON trueであることを忘れないでください。

LATERALのマニュアルを読む

ここで回答するものよりも権威があります。

サブクエリではできないこと

LATERAL結合でできることはareですが、(相関した)サブクエリでは(簡単に)できません。相関サブクエリは、裸の関数呼び出し(複数の行を返す場合に結果の行を乗算する)を除き、複数の列および複数の行ではなく、単一の値のみを返すことができます。ただし、特定の集合を返す関数でも、FROM句でのみ使用できます。 Postgres 9.4以降の複数のパラメーターを持つunnest()のような。 マニュアル:

これは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リストの集合を返す関数

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

上記の例に基づいて:

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

比較:

pg 9.6のdbfiddle ここ
pg 10のdbfiddle ここ

誤報を明確にする

マニュアル:

INNERおよびOUTER結合タイプの場合、結合条件、つまりNATURALONjoin_condition、またはUSING[ join_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's コード例が正しい(CROSS JOINは結合条件を必要としない)および @ Attila's である理由  無効でした。

121

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

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クエリの例です。

30
Andomar

まず、 LateralとCross Applyは同じことです 。したがって、クロスアプライについて読むこともできます。 SQL Serverには長年にわたって実装されていたため、Lateralよりも詳細な情報が見つかります。

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

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

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句が原因で通常の結合を使用できません。横方向または相互適用を使用できます 単純な結合条件がない場合

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

9
Atilla Ozgur

誰も指摘していないことの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でこのようなことを行う方法を知っている唯一の方法です。

1