SQLのJOIN
とは何ですか?また異なる型は何ですか?
SQL JOIN
とは何ですか?SQL JOIN
は、2つ以上のデータベーステーブルからデータを取得するためのメソッドです。
SQL JOIN
sは何ですか?合計5つのJOIN
があります。彼らです :
1. JOIN or INNER JOIN
2. OUTER JOIN
2.1 LEFT OUTER JOIN or LEFT JOIN
2.2 RIGHT OUTER JOIN or RIGHT JOIN
2.3 FULL OUTER JOIN or FULL JOIN
3. NATURAL JOIN
4. CROSS JOIN
5. SELF JOIN
この種のJOIN
では、両方のテーブルの条件に一致するすべてのレコードが取得され、両方のテーブルの一致しないレコードは報告されません。
言い換えれば、INNER JOIN
は、以下の単一の事実に基づいています。テーブル内の一致するエントリのみを一覧表示する必要があります。
他のJOIN
キーワードがない(JOIN
、INNER
、OUTER
など)LEFT
はINNER JOIN
です。言い換えれば、JOIN
はINNER JOIN
の構文上の糖です( 結合と内部結合の違い を参照)。
OUTER JOIN
が取得します
1つのテーブルの一致する行と他のテーブルのすべての行のいずれか、またはすべてのテーブルのすべての行(一致があるかどうかは関係ありません)。
外部結合には3種類あります。
2.1左外部結合または左結合
この結合は、右側のテーブルから一致する行と合わせて左側のテーブルからすべての行を返します。右側のテーブルに一致する列がない場合は、NULL
の値が返されます。
2.2 RIGHT OUTER JOINまたはRIGHT JOIN
このJOIN
は、左側のテーブルから一致する行とともに、右側のテーブルからすべての行を返します。左側のテーブルに一致する列がない場合は、NULL
の値が返されます。
2.3 FULL OUTER JOINまたはFULL JOIN
このJOIN
はLEFT OUTER JOIN
とRIGHT OUTER JOIN
を組み合わせたものです。条件が満たされるとどちらかのテーブルから行を返し、一致しない場合はNULL
値を返します。
言い換えれば、OUTER JOIN
は次の事実に基づいています。テーブルの1つ(RIGHTまたはLEFT)の一致エントリのみ、またはテーブルの両方(FULL)がリストされている必要があります。
Note that `OUTER JOIN` is a loosened form of `INNER JOIN`.
これは2つの条件に基づいています。
JOIN
は、同等の名前を持つすべての列に対して作成されます。これは本質的により理論的であるように思われ、その結果(おそらく)ほとんどのDBMSはこれをサポートすることさえ気にしません。
これは関係する2つの表のデカルト積です。 CROSS JOIN
の結果は、ほとんどの状況では意味がありません。さらに、これはまったく必要ありません(正確に言うと最低限必要です)。
これはJOIN
の別の形式ではなく、それ自体がテーブルのJOIN
(INNER
、OUTER
など)です。
JOIN
句に使用される演算子に応じて、2種類のJOIN
があります。彼らです
どんなJOIN
型(INNER
、OUTER
など)についても、等号演算子(=)のみを使用する場合、JOIN
はEQUI JOIN
となります。
これはEQUI JOIN
と同じですが、>、<、> =などの他のすべての演算子を使用できます。
多くの人が
EQUI JOIN
とThetaJOIN
をINNER
、OUTER
などJOIN
sと同様に考えています。しかし、それは間違いだと思い、アイデアをあいまいにします。INNER JOIN
、OUTER JOIN
などはすべてテーブルとそのデータに接続されているのに対し、EQUI JOIN
とTHETA JOIN
は前者で使用している演算子にのみ接続されています。繰り返しますが、
NATURAL JOIN
をある種の「独特の」EQUI JOIN
と見なす人はたくさんいます。実際、私がNATURAL JOIN
について述べた最初の条件のために、それは真実です。しかし、それを単にNATURAL JOIN
sだけに制限する必要はありません。INNER JOIN
s、OUTER JOIN
sなどはEQUI JOIN
でも構いません。
定義:
JOINSは、複数のテーブルから同時に結合されたデータを同時にクエリする方法です。
RDBMSには、5種類の結合があります。
Equi-Join: 等価条件に基づいて2つのテーブルからの共通レコードを結合します。技術的には、結合は1つのテーブルの主キーの値と別のテーブルの外部キーの値を比較するために等号演算子(=)を使用して行われるため、結果セットには両方のテーブルの共通(一致)レコードが含まれます。実装については、INNER-JOINを参照してください。
Natural-Join: これはEqui-Joinの拡張バージョンで、SELECT操作で重複列が省略されます。実装については、INNER-JOINを参照してください。
非等結合: 結合条件が等号演算子(=)以外で使用されている場合は、等結合の逆です。例えば、!=、<=、> =、>、<、BETWEENなど。実装はINNER-JOINを参照してください。
自己結合: :テーブルがそれ自体と結合する結合のカスタマイズされた動作。これは通常、自己参照テーブル(または単項関係エンティティ)を照会するために必要です。実装についてはINNER-JOINを参照してください。
デカルト積: 条件なしで両方のテーブルのすべてのレコードをクロス結合します。技術的には、WHERE句を使用せずにクエリの結果セットを返します。
SQLに関する懸念と進歩により、3種類の結合があり、これらの結合を使用してすべてのRDBMS結合を実現できます。
内部結合: 2つのテーブルから一致した行をマージ(または結合)します。マッチングは、テーブルの共通の列とそれらの比較操作に基づいて行われます。等式ベースの条件の場合:EQUI-JOINが実行され、それ以外の場合はNon-EQUI-Join。
** OUTER-JOIN:** 2つのテーブルから一致した行とNULL値を持つ一致しない行をマージ(または結合)します。しかし、不一致行の選択をカスタマイズすることができます。たとえば、LEFT OUTER JOINとRIGHT OUTER JOINのサブタイプによって、最初のテーブルまたは2番目のテーブルから一致しない行を選択することができます。
2.1。 LEFT JOIN (別名、LEFT-JOIN):一致した行を2つのテーブルから構成し、LEFTテーブル(つまり最初のテーブル)とのみ一致させない。
2.2。 RIGHT Outer JOIN (別名、RIGHT-JOIN):2つのテーブルから一致した行を返し、RIGHTテーブルからのみ一致しない行を返します。
2.3。 FULL OUTER JOIN (別名OUTER JOIN):両方のテーブルから一致および不一致を返します。
CROSS-JOIN: この結合はマージ/結合されず、代わりにデカルト積を実行します。
注:自己結合は、要件に基づいてINNER-JOIN、OUTER-JOIN、およびCROSS-JOINのいずれかによって実現できますが、テーブルはそれ自体と結合する必要があります。
1.1:内部結合:等結合の実装
SELECT *
FROM Table1 A
INNER JOIN Table2 B ON A.<Primary-Key> =B.<Foreign-Key>;
1.2:INNER-JOIN:Natural-JOINの実装
Select A.*, B.Col1, B.Col2 --But no B.ForeignKeyColumn in Select
FROM Table1 A
INNER JOIN Table2 B On A.Pk = B.Fk;
1.3:NON-Equi-join実装のあるINNER-JOIN
Select *
FROM Table1 A INNER JOIN Table2 B On A.Pk <= B.Fk;
1.4:SELF-JOINを含む内部結合
Select *
FROM Table1 A1 INNER JOIN Table1 A2 On A1.Pk = A2.Fk;
2.1:OUTER JOIN(完全外部結合)
Select *
FROM Table1 A FULL OUTER JOIN Table2 B On A.Pk = B.Fk;
2.2:左結合
Select *
FROM Table1 A LEFT OUTER JOIN Table2 B On A.Pk = B.Fk;
2.3:右結合
Select *
FROM Table1 A RIGHT OUTER JOIN Table2 B On A.Pk = B.Fk;
3.1:CROSS JOIN
Select *
FROM TableA CROSS JOIN TableB;
3.2:CROSS JOIN - 自己JOIN
Select *
FROM Table1 A1 CROSS JOIN Table1 A2;
//または//
Select *
FROM Table1 A1,Table1 A2;
興味深いことに、他のほとんどの答えはこれら二つの問題に苦しんでいます。
私は最近、このトピックに関する記事を書きました: SQLでテーブルを結合するための多種多様な方法へのおそらく不完全な、包括的なガイド _、ここで要約します。
JOINが2つの結合されたテーブルの間に デカルト積 を作成するので、ベン図がそれらをそれほど不正確に説明する理由はここにあります。 Wikipediaはそれをうまく説明しています。
デカルト積のSQL構文はCROSS JOIN
です。例えば:
SELECT *
-- This just generates all the days in January 2017
FROM generate_series(
'2017-01-01'::TIMESTAMP,
'2017-01-01'::TIMESTAMP + INTERVAL '1 month -1 day',
INTERVAL '1 day'
) AS days(day)
-- Here, we're combining all days with all departments
CROSS JOIN departments
あるテーブルのすべての行と他のテーブルのすべての行が結合されます。
ソース:
+--------+ +------------+
| day | | department |
+--------+ +------------+
| Jan 01 | | Dept 1 |
| Jan 02 | | Dept 2 |
| ... | | Dept 3 |
| Jan 30 | +------------+
| Jan 31 |
+--------+
結果:
+--------+------------+
| day | department |
+--------+------------+
| Jan 01 | Dept 1 |
| Jan 01 | Dept 2 |
| Jan 01 | Dept 3 |
| Jan 02 | Dept 1 |
| Jan 02 | Dept 2 |
| Jan 02 | Dept 3 |
| ... | ... |
| Jan 31 | Dept 1 |
| Jan 31 | Dept 2 |
| Jan 31 | Dept 3 |
+--------+------------+
テーブルのカンマ区切りリストを書くだけでも、同じ結果が得られます。
-- CROSS JOINing two tables:
SELECT * FROM table1, table2
INNER JOIN
は、フィルタ述語がリレーショナル代数でTheta
と呼ばれる、単なるフィルタ処理されたCROSS JOIN
です。
例えば:
SELECT *
-- Same as before
FROM generate_series(
'2017-01-01'::TIMESTAMP,
'2017-01-01'::TIMESTAMP + INTERVAL '1 month -1 day',
INTERVAL '1 day'
) AS days(day)
-- Now, exclude all days/departments combinations for
-- days before the department was created
JOIN departments AS d ON day >= d.created_at
キーワードINNER
はオプションです(MS Accessの場合を除く)。
特別な種類のTheta-JOINはequi JOINで、これが最もよく使われています。述部は、ある表の主キーを別の表の外部キーと結合します。説明のために Sakila database を使うと、次のように書くことができます。
SELECT *
FROM actor AS a
JOIN film_actor AS fa ON a.actor_id = fa.actor_id
JOIN film AS f ON f.film_id = fa.film_id
これはすべての俳優を彼らの映画と結合します。
あるいは、いくつかのデータベースでは:
SELECT *
FROM actor
JOIN film_actor USING (actor_id)
JOIN film USING (film_id)
USING()
構文は、JOIN操作のテーブルの両側に存在しなければならない列を指定することを可能にし、それら2つの列に等価述語を作成します。
他の回答では、この「JOINタイプ」を別々にリストしていますが、それは意味がありません。これは、equi JOINの単なる構文シュガー形式です。これは、Theta-JOINまたはINNER JOINの特殊なケースです。 NATURAL JOINは、結合されている両方のテーブルに共通の all 列を単純に収集し、それらの列をUSING()
結合します。偶然の一致( Sakilaデータベース のLAST_UPDATE
列のようなもの)のため、これはほとんど役に立ちません。
構文は次のとおりです。
SELECT *
FROM actor
NATURAL JOIN film_actor
NATURAL JOIN film
OUTER JOIN
は、いくつかのデカルト積のUNION
を作成するため、INNER JOIN
とは少し異なります。我々は書ける:
-- Convenient syntax:
SELECT *
FROM a LEFT JOIN b ON <predicate>
-- Cumbersome, equivalent syntax:
SELECT a.*, b.*
FROM a JOIN b ON <predicate>
UNION ALL
SELECT a.*, NULL, NULL, ..., NULL
FROM a
WHERE NOT EXISTS (
SELECT * FROM b WHERE <predicate>
)
誰も後者を書きたくないので、私たちはOUTER JOIN
を書きます(これは通常データベースによってよりよく最適化されます)。
INNER
と同様に、ここではキーワードOUTER
はオプションです。
OUTER JOIN
には3つの種類があります。
LEFT [ OUTER ] JOIN
:JOIN
式の左側のテーブルは、上に示したように共用体に追加されます。RIGHT [ OUTER ] JOIN
:JOIN
式の右側の表が、上に示したように共用体に追加されます。FULL [ OUTER ] JOIN
:上記のように、JOIN
式の両方のテーブルが共用体に追加されます。これらすべてをキーワードUSING()
またはNATURAL
と組み合わせることができます( 最近実際にはNATURAL FULL JOIN
のユースケースがありました )
OracleとSQL Serverには、歴史的で非推奨の構文がいくつかあります。これは、SQL標準でこの構文が使用される以前から、OUTER JOIN
をサポートしていました。
-- Oracle
SELECT *
FROM actor a, film_actor fa, film f
WHERE a.actor_id = fa.actor_id(+)
AND fa.film_id = f.film_id(+)
-- SQL Server
SELECT *
FROM actor a, film_actor fa, film f
WHERE a.actor_id *= fa.actor_id
AND fa.film_id *= f.film_id
そうは言っても、この構文は使わないでください。あなたが古いブログ記事/レガシーコードからそれを認識することができるように、私はここにこれをリストするだけです。
OUTER JOIN
これを知っている人はほとんどいませんが、SQL標準ではパーティション化されたOUTER JOIN
を指定しています(そしてOracleがそれを実装しています)。あなたはこのようなことを書くことができます:
WITH
-- Using CONNECT BY to generate all dates in January
days(day) AS (
SELECT DATE '2017-01-01' + LEVEL - 1
FROM dual
CONNECT BY LEVEL <= 31
),
-- Our departments
departments(department, created_at) AS (
SELECT 'Dept 1', DATE '2017-01-10' FROM dual UNION ALL
SELECT 'Dept 2', DATE '2017-01-11' FROM dual UNION ALL
SELECT 'Dept 3', DATE '2017-01-12' FROM dual UNION ALL
SELECT 'Dept 4', DATE '2017-04-01' FROM dual UNION ALL
SELECT 'Dept 5', DATE '2017-04-02' FROM dual
)
SELECT *
FROM days
LEFT JOIN departments
PARTITION BY (department) -- This is where the magic happens
ON day >= created_at
結果の一部:
+--------+------------+------------+
| day | department | created_at |
+--------+------------+------------+
| Jan 01 | Dept 1 | | -- Didn't match, but still get row
| Jan 02 | Dept 1 | | -- Didn't match, but still get row
| ... | Dept 1 | | -- Didn't match, but still get row
| Jan 09 | Dept 1 | | -- Didn't match, but still get row
| Jan 10 | Dept 1 | Jan 10 | -- Matches, so get join result
| Jan 11 | Dept 1 | Jan 10 | -- Matches, so get join result
| Jan 12 | Dept 1 | Jan 10 | -- Matches, so get join result
| ... | Dept 1 | Jan 10 | -- Matches, so get join result
| Jan 31 | Dept 1 | Jan 10 | -- Matches, so get join result
ここで重要なのは、JOIN
が「結合の反対側」の何かに一致したかどうかにかかわらず、結合の分割された側からのすべての行が結果に含まれることです。手短に言うと、これはまれなデータをレポートに記入することです。非常に便利!
真剣に?他の答えはこれを得た?もちろん、 SQLにはネイティブの構文がないので残念ながら _(以下のANTI JOINとまったく同じ)。しかし、IN()
とEXISTS()
を使うことができます。映画に出演したすべての俳優を見つけるために:
SELECT *
FROM actor a
WHERE EXISTS (
SELECT * FROM film_actor fa
WHERE a.actor_id = fa.actor_id
)
WHERE a.actor_id = fa.actor_id
述部は準結合述部として機能します。あなたがそれを信じていないなら、実行計画をチェックしてください。 Oracleで。データベースはEXISTS()
述語ではなく、SEMI JOIN操作を実行することがわかります。
これはSEMI JOIN( NOT IN
を使用しないように注意してください _)の正反対です。これには重要な注意点があります。
これは映画のないすべての俳優です:
SELECT *
FROM actor a
WHERE NOT EXISTS (
SELECT * FROM film_actor fa
WHERE a.actor_id = fa.actor_id
)
一部の人々(特にMySQLの人々)もANTI JOINをこのように書いています。
SELECT *
FROM actor a
LEFT JOIN film_actor fa
USING (actor_id)
WHERE film_id IS NULL
歴史的な理由はパフォーマンスだと思います。
OMG、これはクールすぎます。私がそれを言及する唯一の人ですか?これはかっこいいクエリです。
SELECT a.first_name, a.last_name, f.*
FROM actor AS a
LEFT OUTER JOIN LATERAL (
SELECT f.title, SUM(amount) AS revenue
FROM film AS f
JOIN film_actor AS fa USING (film_id)
JOIN inventory AS i USING (film_id)
JOIN rental AS r USING (inventory_id)
JOIN payment AS p USING (rental_id)
WHERE fa.actor_id = a.actor_id -- JOIN predicate with the outer query!
GROUP BY f.film_id
ORDER BY revenue DESC
LIMIT 5
) AS f
ON true
それは俳優あたりの映画を生み出すトップ5の収入を見つけるでしょう。 TOP-N-somethingというクエリが必要になるたびに、LATERAL JOIN
があなたの友達になります。あなたがSQL Serverの人であれば、あなたはJOIN
という名前でこのAPPLY
型を知っています。
SELECT a.first_name, a.last_name, f.*
FROM actor AS a
OUTER APPLY (
SELECT f.title, SUM(amount) AS revenue
FROM film AS f
JOIN film_actor AS fa ON f.film_id = fa.film_id
JOIN inventory AS i ON f.film_id = i.film_id
JOIN rental AS r ON i.inventory_id = r.inventory_id
JOIN payment AS p ON r.rental_id = p.rental_id
WHERE fa.actor_id = a.actor_id -- JOIN predicate with the outer query!
GROUP BY f.film_id
ORDER BY revenue DESC
LIMIT 5
) AS f
LATERAL JOIN
式またはAPPLY
式は、実際には複数の行を生成する「相関副照会」であるためです。しかし、「相関サブクエリ」を許可すれば、次のことについても話すことができます。
これは実際にはOracleとInformixによってのみ実装されています(私の知る限り)が、PostgreSQLでは配列やXMLを使用し、SQL ServerではXMLを使用してエミュレートできます。
MULTISET
は、相関副照会を作成し、結果として生じる行のセットを外部照会にネストします。以下のクエリはすべての俳優を選択し、各俳優についてネストされたコレクションに自分の映画を収集します。
SELECT a.*, MULTISET (
SELECT f.*
FROM film AS f
JOIN film_actor AS fa USING (film_id)
WHERE a.actor_id = fa.actor_id
) AS films
FROM actor
あなたが見たように、普通は言及される単なる "つまらない" INNER
、OUTER
、およびCROSS JOIN
より多くのタイプのJOINがあります。 私の記事で詳細 。そして、それらを説明するためにベン図を使うのをやめてください。