Postgres がLATERALジョインを実行できるようになったので、私はそれについて調べてきました。現在、私は現在、クエリ全体に 4 分以上かかる非効率なサブクエリを多数使用して、チームのために複雑なデータ ダンプを実行しているためです。
LATERAL結合が役立つ可能性があることは理解していますが、Heap Analytics のこのような記事を読んだ後でも、まだ理解できていません。
LATERAL結合の使用例は何ですか? LATERAL結合とサブクエリの違いは何ですか?
Postgres がLATERALジョインを実行できるようになったので、私はそれについて調べてきました。現在、私は現在、クエリ全体に 4 分以上かかる非効率なサブクエリを多数使用して、チームのために複雑なデータ ダンプを実行しているためです。
LATERAL結合が役立つ可能性があることは理解していますが、Heap Analytics のこのような記事を読んだ後でも、まだ理解できていません。
LATERAL結合の使用例は何ですか? LATERAL結合とサブクエリの違いは何ですか?
LATERALこの機能は PostgreSQL 9.3 で導入されました。マニュアル:
に現れるサブクエリ
FROMの前にキーワード を付けることができますLATERAL。FROMこれにより、前の項目で提供された列を参照できます 。( がない場合、各サブクエリは個別に評価されるため、他の項目LATERALを相互参照できません。)FROMに現れるテーブル関数
FROMの前にキーワード を付けることもできますLATERALが、関数の場合、キーワードはオプションです。関数の引数にはFROM、いずれの場合でも、前の項目によって提供される列への参照を含めることができます。
そこには基本的なコード例が示されています。
LATERAL結合は、単純なサブクエリではなく相関サブクエリに似ています。LATERAL結合の右側の式は、その左の行ごとに 1 回評価されます (相関サブクエリと同様)。一方、単純なサブクエリ (テーブル式) は1 回評価されます。それだけ。(ただし、クエリプランナーには、どちらのパフォーマンスも最適化する方法があります。)
同じ問題を解決する、両方のコード例を並べた関連する回答:
複数の列を返す場合、LATERAL結合は通常、よりシンプルでクリーンで高速です。
また、相関サブクエリに相当するものは次のLEFT JOIN LATERAL ... ON trueとおりです。
結合でできることはあります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()のようなセットを返す関数を直接使用することもできます。これは、 Postgres 9.6 まで、同じリストSELECTに複数のそのような関数があると驚くべき動作を示していました。しかし、最終的に Postgres 10 でサニタイズされ、 (標準 SQL でなくても) 有効な代替手段になりました。見る:SELECT
上記の例に基づいて構築:
SELECT *, unnest(a1) AS elem1, unnest(a2) AS elem2
FROM tbl;
比較:
pg 9.6 のdbfiddle はこちら
pg 10 の dbfiddle はこちら
INNERおよびOUTER結合タイプの場合、結合条件を指定する必要があります。つまり、、NATURALjoin_conditionON、または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 のコード例は正しく ( はCROSS JOIN結合条件を必要としません)、Attilaのコード例はそう ではありませんでした。
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
blogプラットフォームでホストされているブログを格納する次のデータベース テーブルを用意します。
また、現在ホストされている 2 つのブログがあります。
| ID | 作成日 | 題名 | URL |
|---|---|---|---|
| 1 | 2013-09-30 | Vlad Mihalcea のブログ | https://vladmihalcea.com |
| 2 | 2017-01-22 | 過敏症 | https://hypersistence.io |
blogテーブルから次のデータを抽出するレポートを作成する必要があります。
PostgreSQL を使用している場合は、次の SQL クエリを実行する必要があります。
SELECT
b.id as blog_id,
extract(
YEAR FROM age(now(), b.created_on)
) AS age_in_years,
date(
created_on + (
extract(YEAR FROM age(now(), b.created_on)) + 1
) * interval '1 year'
) AS next_anniversary,
date(
created_on + (
extract(YEAR FROM age(now(), b.created_on)) + 1
) * interval '1 year'
) - date(now()) AS days_to_next_anniversary
FROM blog b
ORDER BY blog_id
ご覧のとおり、との値age_in_yearsを計算するときに が必要になるため、 を 3 回定義する必要があります。next_anniversarydays_to_next_anniversary
そして、それがまさに LATERAL JOIN が私たちを助けることができるところです。
次のリレーショナル データベース システムは、LATERAL JOIN構文をサポートしています。
SQL Server はとLATERAL JOINを使用してCROSS APPLYエミュレートできますOUTER APPLY。
LATERAL JOIN を使用すると、値を再利用して、値age_in_yearsを計算するときにそれをさらに渡すことができます。next_anniversarydays_to_next_anniversary
前のクエリは、次のように LATERAL JOIN を使用するように書き直すことができます。
SELECT
b.id as blog_id,
age_in_years,
date(
created_on + (age_in_years + 1) * interval '1 year'
) AS next_anniversary,
date(
created_on + (age_in_years + 1) * interval '1 year'
) - date(now()) AS days_to_next_anniversary
FROM blog b
CROSS JOIN LATERAL (
SELECT
cast(
extract(YEAR FROM age(now(), b.created_on)) AS int
) AS age_in_years
) AS t
ORDER BY blog_id
また、age_in_years値を計算して、next_anniversaryおよびのdays_to_next_anniversary計算に再利用できます。
| blog_id | age_in_years | next_anniversary | days_to_next_anniversary |
|---|---|---|---|
| 1 | 7 | 2021-09-30 | 295 |
| 2 | 3 | 2021-01-22 | 44 |
はるかに良いですよね?
はage_in_years、テーブルのすべてのレコードに対して計算されblogます。したがって、相関サブクエリのように機能しますが、サブクエリ レコードはプライマリ テーブルと結合されるため、サブクエリによって生成された列を参照できます。
まず、ラテラルとクロス適用は同じものです。したがって、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
このクエリでは、制限句のため、通常の結合を使用できません。単純な結合条件がない場合は、 Lateral または Cross Apply を使用できます。
ラテラルまたはクロス適用にはさらに多くの使用法がありますが、これは私が見つけた最も一般的なものです。