PostgresがLATERAL
結合を実行できるようになったので、現在、チーム全体でクエリが4分以上かかる非効率なサブクエリを多数使用して複雑なデータダンプを実行しているので、読み進めています。
LATERAL
結合が私を助けてくれるかもしれないことを理解していますが、Heap Analyticsの this one のような記事を読んだ後でも、私はまだ十分には従いません。
LATERAL
結合の使用例は何ですか? LATERAL
結合とサブクエリの違いは何ですか?
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
結合タイプの場合、結合条件、つまりNATURAL
、ON
join_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 である理由 は 無効でした。
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
クエリの例です。
まず、 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句が原因で通常の結合を使用できません。横方向または相互適用を使用できます 単純な結合条件がない場合 。
ラテラルまたはクロス適用の使用法はもっとありますが、これは私が見つけた最も一般的なものです。
誰も指摘していないことの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でこのようなことを行う方法を知っている唯一の方法です。