サーチ…
前書き
JOINは、2つのテーブルの情報を結合(結合)する方法です。結果は、結合タイプ(INNER / OUTER / CROSSおよびLEFT / RIGHT / FULL、以下で説明します)と結合基準(両方のテーブルの行がどのように関連しているか)によって定義される両方のテーブルのステッチセットです。
テーブルは、それ自体または他のテーブルに結合することができます。 3つ以上のテーブルの情報にアクセスする必要がある場合は、複数の結合をFROM句で指定できます。
構文
[ { INNER | { { LEFT | RIGHT | FULL } [ OUTER ] } } ] JOIN
備考
ジョインは、その名前が示すように、複数のテーブルのデータをクエリする方法であり、行には複数のテーブルから取得した列が表示されます。
基本的な明示的な内部結合
基本結合(「内部結合」とも呼ばれる)は、2つの表のデータをjoin
節で定義された関係で照会します。
次の例では、Employeesテーブルから従業員のファーストネーム(FName)を選択し、Departmentsテーブルから(Name)担当する部門の名前を選択します。
SELECT Employees.FName, Departments.Name
FROM Employees
JOIN Departments
ON Employees.DepartmentId = Departments.Id
これはサンプルデータベースから次のものを返します :
従業員.FName | Departments.Name |
---|---|
ジェームス | HR |
ジョン | HR |
リチャード | 販売 |
暗黙的な結合
結合は、 from
節に複数の表を持ち、カンマで区切り,
where
節でそれらの関係を定義することによっても実行できます。この手法は暗黙結合と呼ばれます(実際にはjoin
句は含まれていないjoin
)。
すべてのRDBMSがそれをサポートしていますが、構文は通常は推奨されていません。この構文を使用することが悪い考えである理由は次のとおりです。
- 間違った結果を返してしまう偶然のクロス結合を取得することは可能です。特に、クエリに多数の結合がある場合は、
- クロスジョインを意図していた場合は、構文から明らかではなく(代わりにCROSS JOINを書きます)、メンテナンス中に誰かがそれを変更する可能性があります。
次の例では、従業員のファーストネームと、自分が働いている部署の名前を選択します。
SELECT e.FName, d.Name
FROM Employee e, Departments d
WHERE e.DeptartmentId = d.Id
これはサンプルデータベースから次のものを返します :
e.FName | d.Name |
---|---|
ジェームス | HR |
ジョン | HR |
リチャード | 販売 |
左外部結合
左外部結合(左結合または外部結合とも呼ばれます)は、左テーブルのすべての行が確実に表現される結合です。右側の表の一致する行が存在しない場合、対応するフィールドはNULL
です。
次の例では、すべての部門とその部門で働く従業員のファーストネームを選択します。従業員のいない部署は依然として結果に返されますが、従業員名はNULLになります。
SELECT Departments.Name, Employees.FName
FROM Departments
LEFT OUTER JOIN Employees
ON Departments.Id = Employees.DepartmentId
これはサンプルデータベースから次のものを返します :
Departments.Name | 従業員.FName |
---|---|
HR | ジェームス |
HR | ジョン |
HR | ジョンナトン |
販売 | マイケル |
テック | ヌル |
それではどうやって動くの?
FROM句には2つのテーブルがあります。
イド | FName | LName | 電話番号 | マネージャーID | DepartmentId | 給料 | HireDate |
---|---|---|---|---|---|---|---|
1 | ジェームス | スミス | 1234567890 | ヌル | 1 | 1000 | 01-01-2002 |
2 | ジョン | ジョンソン | 2468101214 | 1 | 1 | 400 | 23-03-2005 |
3 | マイケル | ウィリアムズ | 1357911131 | 1 | 2 | 600 | 12-05-2009 |
4 | ジョンナトン | スミス | 1212121212 | 2 | 1 | 500 | 24-07-2016 |
そして
イド | 名 |
---|---|
1 | HR |
2 | 販売 |
3 | テック |
最初に、中間テーブルを与える2つのテーブルからデカルト積が生成される。
結合条件を満たすレコード( Departments.Id = Employees.DepartmentId )は太字で強調表示されています。これらはクエリの次の段階に渡されます。
これはLEFT OUTER JOINであるため、すべてのレコードは結合のLEFT側(Departments)から戻されますが、RIGHT側のレコードは結合基準と一致しない場合はNULLマーカーが与えられます。下の表では、これはNULL
TechをNULL
イド | 名 | イド | FName | LName | 電話番号 | マネージャーID | DepartmentId | 給料 | HireDate |
---|---|---|---|---|---|---|---|---|---|
1 | HR | 1 | ジェームス | スミス | 1234567890 | ヌル | 1 | 1000 | 01-01-2002 |
1 | HR | 2 | ジョン | ジョンソン | 2468101214 | 1 | 1 | 400 | 23-03-2005 |
1 | HR | 3 | マイケル | ウィリアムズ | 1357911131 | 1 | 2 | 600 | 12-05-2009 |
1 | HR | 4 | ジョンナトン | スミス | 1212121212 | 2 | 1 | 500 | 24-07-2016 |
2 | 販売 | 1 | ジェームス | スミス | 1234567890 | ヌル | 1 | 1000 | 01-01-2002 |
2 | 販売 | 2 | ジョン | ジョンソン | 2468101214 | 1 | 1 | 400 | 23-03-2005 |
2 | 販売 | 3 | マイケル | ウィリアムズ | 1357911131 | 1 | 2 | 600 | 12-05-2009 |
2 | 販売 | 4 | ジョンナトン | スミス | 1212121212 | 2 | 1 | 500 | 24-07-2016 |
3 | テック | 1 | ジェームス | スミス | 1234567890 | ヌル | 1 | 1000 | 01-01-2002 |
3 | テック | 2 | ジョン | ジョンソン | 2468101214 | 1 | 1 | 400 | 23-03-2005 |
3 | テック | 3 | マイケル | ウィリアムズ | 1357911131 | 1 | 2 | 600 | 12-05-2009 |
3 | テック | 4 | ジョンナトン | スミス | 1212121212 | 2 | 1 | 500 | 24-07-2016 |
最後に、 SELECT句内で使用される各式は、最終テーブルを返すように評価されます。
Departments.Name | 従業員.FName |
---|---|
HR | ジェームス |
HR | ジョン |
販売 | リチャード |
テック | ヌル |
自己結合
テーブルは、それ自身に結合されていてもよい。この使用例では、表の2つの出現を区別するためにエイリアスを使用する必要があります。
以下の例では、 サンプルデータベースEmployeesテーブルの Employeeごとに、従業員のマネージャの対応するファーストネームとともに従業員の名を含むレコードが返されます。マネージャーも従業員であるため、テーブル自体は従業員です。
SELECT
e.FName AS "Employee",
m.FName AS "Manager"
FROM
Employees e
JOIN
Employees m
ON e.ManagerId = m.Id
このクエリは、次のデータを返します。
従業員 | マネージャー |
---|---|
ジョン | ジェームス |
マイケル | ジェームス |
ジョンナトン | ジョン |
それではどうやって動くの?
元の表には次のレコードが含まれています。
イド | FName | LName | 電話番号 | マネージャーID | DepartmentId | 給料 | HireDate |
---|---|---|---|---|---|---|---|
1 | ジェームス | スミス | 1234567890 | ヌル | 1 | 1000 | 01-01-2002 |
2 | ジョン | ジョンソン | 2468101214 | 1 | 1 | 400 | 23-03-2005 |
3 | マイケル | ウィリアムズ | 1357911131 | 1 | 2 | 600 | 12-05-2009 |
4 | ジョンナトン | スミス | 1212121212 | 2 | 1 | 500 | 24-07-2016 |
最初のアクションは、 FROM句で使用されるテーブル内のすべてのレコードのデカルト積を作成することです。この場合、Employeesテーブルが2回あるので、中間テーブルは次のようになります(この例では使用されていないフィールドはすべて削除しています)。
e.Id | e.FName | e.ManagerId | m.Id | m.FName | m.ManagerId |
---|---|---|---|---|---|
1 | ジェームス | ヌル | 1 | ジェームス | ヌル |
1 | ジェームス | ヌル | 2 | ジョン | 1 |
1 | ジェームス | ヌル | 3 | マイケル | 1 |
1 | ジェームス | ヌル | 4 | ジョンナトン | 2 |
2 | ジョン | 1 | 1 | ジェームス | ヌル |
2 | ジョン | 1 | 2 | ジョン | 1 |
2 | ジョン | 1 | 3 | マイケル | 1 |
2 | ジョン | 1 | 4 | ジョンナトン | 2 |
3 | マイケル | 1 | 1 | ジェームス | ヌル |
3 | マイケル | 1 | 2 | ジョン | 1 |
3 | マイケル | 1 | 3 | マイケル | 1 |
3 | マイケル | 1 | 4 | ジョンナトン | 2 |
4 | ジョンナトン | 2 | 1 | ジェームス | ヌル |
4 | ジョンナトン | 2 | 2 | ジョン | 1 |
4 | ジョンナトン | 2 | 3 | マイケル | 1 |
4 | ジョンナトン | 2 | 4 | ジョンナトン | 2 |
次のアクションは、 JOIN基準を満たすレコードのみを保持することです。エイリアス化されたe
テーブルのManagerId
がエイリアス化されたm
テーブルId
と等しいレコード:
e.Id | e.FName | e.ManagerId | m.Id | m.FName | m.ManagerId |
---|---|---|---|---|---|
2 | ジョン | 1 | 1 | ジェームス | ヌル |
3 | マイケル | 1 | 1 | ジェームス | ヌル |
4 | ジョンナトン | 2 | 2 | ジョン | 1 |
次に、 SELECT句内で使用される各式が評価され、この表が戻されます。
e.FName | m.FName |
---|---|
ジョン | ジェームス |
マイケル | ジェームス |
ジョンナトン | ジョン |
最後に、列名e.FName
とm.FName
は、 AS演算子で割り当てられたエイリアス列名に置き換えられます。
従業員 | マネージャー |
---|---|
ジョン | ジェームス |
マイケル | ジェームス |
ジョンナトン | ジョン |
クロスジョイン
クロス・ジョインは、2つのメンバーのデカルト積を行います。デカルト積は、1つの表の各行が結合の2番目の表の各行と結合されることを意味します。たとえば、 TABLEA
に20行、 TABLEB
に20行がある場合、結果は20*20 = 400
出力行になります。
SELECT d.Name, e.FName
FROM Departments d
CROSS JOIN Employees e;
返すもの:
d.Name | e.FName |
---|---|
HR | ジェームス |
HR | ジョン |
HR | マイケル |
HR | ジョンナトン |
販売 | ジェームス |
販売 | ジョン |
販売 | マイケル |
販売 | ジョンナトン |
テック | ジェームス |
テック | ジョン |
テック | マイケル |
テック | ジョンナトン |
デカルト結合を行いたい場合は、明示的なCROSS JOINを書くことをお勧めします。
サブクエリへの結合
サブクエリの結合は、子/詳細テーブルから集計データを取得し、それを親テーブルまたはヘッダーテーブルのレコードとともに表示する場合によく使用されます。たとえば、子レコードの数、子レコードの数値列の平均、または日付または数値フィールドに基づく上または下の行を取得することができます。この例では、エイリアスを使用しています。これにより、複数のテーブルが関わっている場合に、クエリが読みやすくなります。かなり典型的なサブクエリ結合のようなものがあります。この場合、親テーブルPurchase Ordersからすべての行を取得し、子テーブルPurchaseOrderLineItemsの各親レコードの最初の行のみを取得しています。
SELECT po.Id, po.PODate, po.VendorName, po.Status, item.ItemNo,
item.Description, item.Cost, item.Price
FROM PurchaseOrders po
LEFT JOIN
(
SELECT l.PurchaseOrderId, l.ItemNo, l.Description, l.Cost, l.Price, Min(l.id) as Id
FROM PurchaseOrderLineItems l
GROUP BY l.PurchaseOrderId, l.ItemNo, l.Description, l.Cost, l.Price
) AS item ON item.PurchaseOrderId = po.Id
適用されている&結合されていない
非常に興味深いタイプのJOINは、LATERAL JOIN(PostgreSQL 9.3以降の新機能)です。
これはSQL-Server&OracleのCROSS APPLY / OUTER APPLYとも呼ばれます。
基本的な考え方は、テーブル値関数(またはインラインサブクエリ)が、参加するすべての行に適用されるということです。
これにより、たとえば、最初に一致するエントリだけを別のテーブルに結合することができます。
通常の結合と横方向の結合の違いは、以前に結合したサブクエリに結合した列を使用できるという点にあります。
構文:
PostgreSQL 9.3+
左|右|内部JOIN LATERAL
SQLサーバー:
クロス| OUTERが適用されます
INNER JOIN LATERAL
はCROSS APPLY
と同じです
LEFT JOIN LATERAL
はOUTER APPLY
と同じです
使用例(PostgreSQL 9.3以降):
SELECT * FROM T_Contacts
--LEFT JOIN T_MAP_Contacts_Ref_OrganisationalUnit ON MAP_CTCOU_CT_UID = T_Contacts.CT_UID AND MAP_CTCOU_SoftDeleteStatus = 1
--WHERE T_MAP_Contacts_Ref_OrganisationalUnit.MAP_CTCOU_UID IS NULL -- 989
LEFT JOIN LATERAL
(
SELECT
--MAP_CTCOU_UID
MAP_CTCOU_CT_UID
,MAP_CTCOU_COU_UID
,MAP_CTCOU_DateFrom
,MAP_CTCOU_DateTo
FROM T_MAP_Contacts_Ref_OrganisationalUnit
WHERE MAP_CTCOU_SoftDeleteStatus = 1
AND MAP_CTCOU_CT_UID = T_Contacts.CT_UID
/*
AND
(
(__in_DateFrom <= T_MAP_Contacts_Ref_OrganisationalUnit.MAP_KTKOE_DateTo)
AND
(__in_DateTo >= T_MAP_Contacts_Ref_OrganisationalUnit.MAP_KTKOE_DateFrom)
)
*/
ORDER BY MAP_CTCOU_DateFrom
LIMIT 1
) AS FirstOE
SQL Serverの場合
SELECT * FROM T_Contacts
--LEFT JOIN T_MAP_Contacts_Ref_OrganisationalUnit ON MAP_CTCOU_CT_UID = T_Contacts.CT_UID AND MAP_CTCOU_SoftDeleteStatus = 1
--WHERE T_MAP_Contacts_Ref_OrganisationalUnit.MAP_CTCOU_UID IS NULL -- 989
-- CROSS APPLY -- = INNER JOIN
OUTER APPLY -- = LEFT JOIN
(
SELECT TOP 1
--MAP_CTCOU_UID
MAP_CTCOU_CT_UID
,MAP_CTCOU_COU_UID
,MAP_CTCOU_DateFrom
,MAP_CTCOU_DateTo
FROM T_MAP_Contacts_Ref_OrganisationalUnit
WHERE MAP_CTCOU_SoftDeleteStatus = 1
AND MAP_CTCOU_CT_UID = T_Contacts.CT_UID
/*
AND
(
(@in_DateFrom <= T_MAP_Contacts_Ref_OrganisationalUnit.MAP_KTKOE_DateTo)
AND
(@in_DateTo >= T_MAP_Contacts_Ref_OrganisationalUnit.MAP_KTKOE_DateFrom)
)
*/
ORDER BY MAP_CTCOU_DateFrom
) AS FirstOE
フル・ジョイン
あまり知られていないJOINの1つのタイプは、FULL JOINです。
(注:FULL JOINは2016年のMySQLではサポートされていません)
FULL OUTER JOINは、左の表のすべての行と、右の表のすべての行を戻します。
左側のテーブルに、右側のテーブルに一致する行がない場合や、左側のテーブルに一致する行がない場合は、それらの行もリストされます。
例1:
SELECT * FROM Table1
FULL JOIN Table2
ON 1 = 2
例2:
SELECT
COALESCE(T_Budget.Year, tYear.Year) AS RPT_BudgetInYear
,COALESCE(T_Budget.Value, 0.0) AS RPT_Value
FROM T_Budget
FULL JOIN tfu_RPT_All_CreateYearInterval(@budget_year_from, @budget_year_to) AS tYear
ON tYear.Year = T_Budget.Year
ソフト削除を使用している場合は、WHERE節でソフト削除ステータスを再度チェックする必要があります(FULL JOINはUNIONのように動作するため)。
あなたはAP_SoftDeleteStatus = 1をjoin節に置くので、この小さな事実を見落とすのは簡単です。
また、FULL JOINを実行している場合は、通常、WHERE句にNULLを許可する必要があります。値にNULLを許可するのを忘れると、INNER結合と同じ効果があります。これは、FULL JOINを実行している場合に望ましくないものです。
例:
SELECT
T_AccountPlan.AP_UID
,T_AccountPlan.AP_Code
,T_AccountPlan.AP_Lang_EN
,T_BudgetPositions.BUP_Budget
,T_BudgetPositions.BUP_UID
,T_BudgetPositions.BUP_Jahr
FROM T_BudgetPositions
FULL JOIN T_AccountPlan
ON T_AccountPlan.AP_UID = T_BudgetPositions.BUP_AP_UID
AND T_AccountPlan.AP_SoftDeleteStatus = 1
WHERE (1=1)
AND (T_BudgetPositions.BUP_SoftDeleteStatus = 1 OR T_BudgetPositions.BUP_SoftDeleteStatus IS NULL)
AND (T_AccountPlan.AP_SoftDeleteStatus = 1 OR T_AccountPlan.AP_SoftDeleteStatus IS NULL)
再帰的な結合
再帰的結合は、親子データを取得するためによく使用されます。 SQLでは、これらは再帰的な共通テーブル式で実装されます。たとえば、次のようになります。
WITH RECURSIVE MyDescendants AS (
SELECT Name
FROM People
WHERE Name = 'John Doe'
UNION ALL
SELECT People.Name
FROM People
JOIN MyDescendants ON People.Name = MyDescendants.Parent
)
SELECT * FROM MyDescendants;
内外の結合の違い
SQLには、一致する行が結果に含まれるかどうか( INNER JOIN
、 LEFT OUTER JOIN
、 RIGHT OUTER JOIN
、およびFULL OUTER JOIN
( INNER
およびOUTER
キーワードはオプション))を指定するための様々な結合タイプがあります。下の図は、これらのタイプの結合の違いを示しています。青色の領域は結合によって戻される結果を表し、白い領域は結合が戻らない結果を表します。
SQL Pictorial Presentation( 参照 )とのクロス結合:
以下はこの回答の例です。
たとえば、以下のような2つのテーブルがあります。
A B
- -
1 3
2 4
3 5
4 6
(1,2)はAに固有であり、(3,4)は共通であり、(5,6)はBに固有であることに留意されたい。
内部結合
等価クエリのいずれかを使用する内部結合は、2つのテーブル、すなわちそれらが共通して持つ2つのテーブルの共通部分を与えます。
select * from a INNER JOIN b on a.a = b.b;
select a.*,b.* from a,b where a.a = b.b;
a | b
--+--
3 | 3
4 | 4
左外部結合
左外部結合は、A内のすべての行とB内の共通行を与えます。
select * from a LEFT OUTER JOIN b on a.a = b.b;
a | b
--+-----
1 | null
2 | null
3 | 3
4 | 4
右外部結合
同様に、右外部結合により、B内のすべての行と、A内の共通行が得られます。
select * from a RIGHT OUTER JOIN b on a.a = b.b;
a | b
-----+----
3 | 3
4 | 4
null | 5
null | 6
完全外部結合
完全な外部結合により、AとBの和集合、つまりAのすべての行とBのすべての行が得られます。A内の何かがB内の対応するデータを持たない場合、B部分はnullです。逆に。
select * from a FULL OUTER JOIN b on a.a = b.b;
a | b
-----+-----
1 | null
2 | null
3 | 3
4 | 4
null | 6
null | 5
JOIN用語:内、外、半、アンチ...
たとえば、2つのテーブル(AとB)とその行の一部が一致しているとします(特定のJOIN条件との関連で、特定のケースでは何でも構いません)。
さまざまな結合タイプを使用して、一致または不一致の行をいずれかの側から含めたり除外したり、上の図から対応する用語を選択して正しく名前を付けることができます。
以下の例では、次のテストデータを使用しています。
CREATE TABLE A (
X varchar(255) PRIMARY KEY
);
CREATE TABLE B (
Y varchar(255) PRIMARY KEY
);
INSERT INTO A VALUES
('Amy'),
('John'),
('Lisa'),
('Marco'),
('Phil');
INSERT INTO B VALUES
('Lisa'),
('Marco'),
('Phil'),
('Tim'),
('Vincent');
内部結合
一致する左右の行を結合します。
SELECT * FROM A JOIN B ON X = Y;
X Y
------ -----
Lisa Lisa
Marco Marco
Phil Phil
左外部結合
「左結合」と省略されることもあります。一致する左右の行を結合し、一致しない左の行を含みます。
SELECT * FROM A LEFT JOIN B ON X = Y;
X Y
----- -----
Amy NULL
John NULL
Lisa Lisa
Marco Marco
Phil Phil
右外部結合
「右結合」と省略されることもあります。一致する左右の行を結合し、一致しない右の行を含みます。
SELECT * FROM A RIGHT JOIN B ON X = Y;
X Y
----- -------
Lisa Lisa
Marco Marco
Phil Phil
NULL Tim
NULL Vincent
完全な外部結合
時には「完全結合」と略記されることもある。左外部結合の連合。
SELECT * FROM A FULL JOIN B ON X = Y;
X Y
----- -------
Amy NULL
John NULL
Lisa Lisa
Marco Marco
Phil Phil
NULL Tim
NULL Vincent
左セミ結合
右の行に一致する左の行を含みます。
SELECT * FROM A WHERE X IN (SELECT Y FROM B);
X
-----
Lisa
Marco
Phil
右セミ結合
左の行に一致する右の行が含まれます。
SELECT * FROM B WHERE Y IN (SELECT X FROM A);
Y
-----
Lisa
Marco
Phil
ご覧のとおり、左と右のセミジョインには専用のIN構文がありません.SQLテキスト内のテーブル位置を切り替えるだけで効果が得られます。
左のアンチ・セミ・ジョイン
右側の行と一致しない左側の行が含まれます。
SELECT * FROM A WHERE X NOT IN (SELECT Y FROM B);
X
----
Amy
John
警告: NULL可能カラムでNOT INを使用している場合は注意してください!詳細はこちら 。
右アンチ・セミ・ジョイン
左側の行と一致しない右側の行が含まれます。
SELECT * FROM B WHERE Y NOT IN (SELECT X FROM A);
Y
-------
Tim
Vincent
ご覧のとおり、左と右のアンチ・セミ・ジョインには専用のNOT IN構文がありません - SQLテキスト内のテーブル位置を切り替えるだけで効果が得られます。
クロス結合
すべての右の行とすべての左のデカルト積。
SELECT * FROM A CROSS JOIN B;
X Y
----- -------
Amy Lisa
John Lisa
Lisa Lisa
Marco Lisa
Phil Lisa
Amy Marco
John Marco
Lisa Marco
Marco Marco
Phil Marco
Amy Phil
John Phil
Lisa Phil
Marco Phil
Phil Phil
Amy Tim
John Tim
Lisa Tim
Marco Tim
Phil Tim
Amy Vincent
John Vincent
Lisa Vincent
Marco Vincent
Phil Vincent
クロス結合は、常に一致する結合条件を持つ内部結合に相当します。したがって、次のクエリは同じ結果を返します。
SELECT * FROM A JOIN B ON 1 = 1;
自己結合
これは、単にそれ自身と結合しているテーブルを示します。自己結合は、上で説明した任意の結合型にすることができます。たとえば、これは内部自己結合です。
SELECT * FROM A A1 JOIN A A2 ON LEN(A1.X) < LEN(A2.X);
X X
---- -----
Amy John
Amy Lisa
Amy Marco
John Marco
Lisa Marco
Phil Marco
Amy Phil