web-dev-qa-db-ja.com

SQL JOINとさまざまな種類のJOIN

SQLのJOINとは何ですか?また異なる型は何ですか?

225
M-D

SQL JOINとは何ですか?

SQL JOINは、2つ以上のデータベーステーブルからデータを取得するためのメソッドです。

違うSQL JOINsは何ですか?

合計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

1.参加または内部参加:

この種のJOINでは、両方のテーブルの条件に一致するすべてのレコードが取得され、両方のテーブルの一致しないレコードは報告されません。

言い換えれば、INNER JOINは、以下の単一の事実に基づいています。テーブル内の一致するエントリのみを一覧表示する必要があります。

他のJOINキーワードがない(JOININNEROUTERなど)LEFTINNER JOINです。言い換えれば、JOININNER JOINの構文上の糖です( 結合と内部結合の違い を参照)。

2.外部への参加:

OUTER JOINが取得します

1つのテーブルの一致する行と他のテーブルのすべての行のいずれか、またはすべてのテーブルのすべての行(一致があるかどうかは関係ありません)。

外部結合には3種類あります。

2.1左外部結合または左結合

この結合は、右側のテーブルから一致する行と合わせて左側のテーブルからすべての行を返します。右側のテーブルに一致する列がない場合は、NULLの値が返されます。

2.2 RIGHT OUTER JOINまたはRIGHT JOIN

このJOINは、左側のテーブルから一致する行とともに、右側のテーブルからすべての行を返します。左側のテーブルに一致する列がない場合は、NULLの値が返されます。

2.3 FULL OUTER JOINまたはFULL JOIN

このJOINLEFT OUTER JOINRIGHT OUTER JOINを組み合わせたものです。条件が満たされるとどちらかのテーブルから行を返し、一致しない場合はNULL値を返します。

言い換えれば、OUTER JOINは次の事実に基づいています。テーブルの1つ(RIGHTまたはLEFT)の一致エントリのみ、またはテーブルの両方(FULL)がリストされている必要があります。

Note that `OUTER JOIN` is a loosened form of `INNER JOIN`.

3.自然な参加:

これは2つの条件に基づいています。

  1. JOINは、同等の名前を持つすべての列に対して作成されます。
  2. 結果から重複する列を削除します。

これは本質的により理論的であるように思われ、その結果(おそらく)ほとんどのDBMSはこれをサポートすることさえ気にしません。

4.クロスジョイン:

これは関係する2つの表のデカルト積です。 CROSS JOINの結果は、ほとんどの状況では意味がありません。さらに、これはまったく必要ありません(正確に言うと最低限必要です)。

5.セルフジョイン:

これはJOINの別の形式ではなく、それ自体がテーブルのJOININNEROUTERなど)です。

演算子に基づく結合

JOIN句に使用される演算子に応じて、2種類のJOINがあります。彼らです

  1. エクイジョイン
  2. シータジョイン

1. Equi JOIN:

どんなJOIN型(INNEROUTERなど)についても、等号演算子(=)のみを使用する場合、JOINEQUI JOINとなります。

シータが結合する:

これはEQUI JOINと同じですが、>、<、> =などの他のすべての演算子を使用できます。

多くの人がEQUI JOINとTheta JOININNEROUTERなどJOINsと同様に考えています。しかし、それは間違いだと思い、アイデアをあいまいにします。 INNER JOINOUTER JOINなどはすべてテーブルとそのデータに接続されているのに対し、EQUI JOINTHETA JOINは前者で使用している演算子にのみ接続されています。

繰り返しますが、NATURAL JOINをある種の「独特の」EQUI JOINと見なす人はたくさんいます。実際、私がNATURAL JOINについて述べた最初の条件のために、それは真実です。しかし、それを単にNATURAL JOINsだけに制限する必要はありません。 INNER JOINs、OUTER JOINsなどはEQUI JOINでも構いません。

236
M-D

W3schools :からのイラスト


INNER JOIN - Only records which match the condition in both tables


LEFT JOIN - All records from table 1 in conjunction with records which match the condition in table 2


RIGHT JOIN - All records from table 2 in conjunction with records from table 1 which match the condition


FULL OUTER JOIN - Combination of both Left and Right Outer joins matching ON clause but preserving both tables


319
Anup

定義:


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結合を実現できます。

  1. 内部結合: 2つのテーブルから一致した行をマージ(または結合)します。マッチングは、テーブルの共通の列とそれらの比較操作に基づいて行われます。等式ベースの条件の場合:EQUI-JOINが実行され、それ以外の場合はNon-EQUI-Join。

  2. ** 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):両方のテーブルから一致および不一致を返します。

  3. CROSS-JOIN: この結合はマージ/結合されず、代わりにデカルト積を実行します。

enter image description here 注:自己結合は、要件に基づいて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はデカルト積です

JOINが2つの結合されたテーブルの間に デカルト積 を作成するので、ベン図がそれらをそれほど不正確に説明する理由はここにあります。 Wikipediaはそれをうまく説明しています。

enter image description here

デカルト積の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の場合を除く)。

結果の例については記事を見てください

EQUI JOIN

特別な種類の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 ] JOINJOIN式の左側のテーブルは、上に示したように共用体に追加されます。
  • RIGHT [ OUTER ] JOINJOIN式の右側の表が、上に示したように共用体に追加されます。
  • 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が「結合の反対側」の何かに一致したかどうかにかかわらず、結合の分割された側からのすべての行が結果に含まれることです。手短に言うと、これはまれなデータをレポートに記入することです。非常に便利!

SEMI 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操作を実行することがわかります。

enter image description here

アンチジョイン

これは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

あなたが見たように、普通は言及される単なる "つまらない" INNEROUTER、およびCROSS JOINより多くのタイプのJOINがあります。 私の記事で詳細 。そして、それらを説明するためにベン図を使うのをやめてください。

36
Lukas Eder

私は私の意見では、私は言葉よりよく説明するイラストを作成しました: SQL Join table of explanation

9
Gisway