パート 1 - 結合と結合
この回答は以下をカバーしています:
- パート1
- パート2
- サブクエリ - サブクエリとは何か、使用できる場所、注意すべき点
- デカルトが AKA に参加 - ああ、惨めさ!
データベース内の複数のテーブルからデータを取得するには、いくつかの方法があります。この回答では、ANSI-92 結合構文を使用します。これは、古い ANSI-89 構文を使用する他の多くのチュートリアルとは異なる場合があります (また、89 に慣れている場合は、はるかに直感的ではないように思えるかもしれませんが、私が言えることは、それを試してみることだけです)。クエリがより複雑になり始める時期を理解するために。なぜそれを使用するのですか?パフォーマンスの向上はありますか? 短い答えはノーですが、慣れると読みやすくなります。この構文を使用して他の人が書いたクエリを読む方が簡単です。
また、利用可能な車を追跡するためのデータベースを持つ小さな車庫の概念を使用します。所有者はあなたを IT コンピューターの担当者として雇い、彼が要求するデータをすぐにドロップできることを期待しています。
最終的なテーブルで使用されるルックアップ テーブルをいくつか作成しました。これにより、作業に適したモデルが得られます。まず、次の構造を持つサンプル データベースに対してクエリを実行します。始めるときによくある間違いを考えて、何が問題なのかを説明します。もちろん、それらを修正する方法も示します。
最初の表は単純な色のリストで、自動車置き場にある色がわかります。
mysql> create table colors(id int(3) not null auto_increment primary key,
-> color varchar(15), paint varchar(10));
Query OK, 0 rows affected (0.01 sec)
mysql> show columns from colors;
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| id | int(3) | NO | PRI | NULL | auto_increment |
| color | varchar(15) | YES | | NULL | |
| paint | varchar(10) | YES | | NULL | |
+-------+-------------+------+-----+---------+----------------+
3 rows in set (0.01 sec)
mysql> insert into colors (color, paint) values ('Red', 'Metallic'),
-> ('Green', 'Gloss'), ('Blue', 'Metallic'),
-> ('White' 'Gloss'), ('Black' 'Gloss');
Query OK, 5 rows affected (0.00 sec)
Records: 5 Duplicates: 0 Warnings: 0
mysql> select * from colors;
+----+-------+----------+
| id | color | paint |
+----+-------+----------+
| 1 | Red | Metallic |
| 2 | Green | Gloss |
| 3 | Blue | Metallic |
| 4 | White | Gloss |
| 5 | Black | Gloss |
+----+-------+----------+
5 rows in set (0.00 sec)
ブランド テーブルは、販売可能な自動車のさまざまなブランドを識別します。
mysql> create table brands (id int(3) not null auto_increment primary key,
-> brand varchar(15));
Query OK, 0 rows affected (0.01 sec)
mysql> show columns from brands;
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| id | int(3) | NO | PRI | NULL | auto_increment |
| brand | varchar(15) | YES | | NULL | |
+-------+-------------+------+-----+---------+----------------+
2 rows in set (0.01 sec)
mysql> insert into brands (brand) values ('Ford'), ('Toyota'),
-> ('Nissan'), ('Smart'), ('BMW');
Query OK, 5 rows affected (0.00 sec)
Records: 5 Duplicates: 0 Warnings: 0
mysql> select * from brands;
+----+--------+
| id | brand |
+----+--------+
| 1 | Ford |
| 2 | Toyota |
| 3 | Nissan |
| 4 | Smart |
| 5 | BMW |
+----+--------+
5 rows in set (0.00 sec)
モデル テーブルにはさまざまな種類の車が含まれます。実際の車のモデルではなく、さまざまな種類の車を使用する方が簡単です。
mysql> create table models (id int(3) not null auto_increment primary key,
-> model varchar(15));
Query OK, 0 rows affected (0.01 sec)
mysql> show columns from models;
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| id | int(3) | NO | PRI | NULL | auto_increment |
| model | varchar(15) | YES | | NULL | |
+-------+-------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)
mysql> insert into models (model) values ('Sports'), ('Sedan'), ('4WD'), ('Luxury');
Query OK, 4 rows affected (0.00 sec)
Records: 4 Duplicates: 0 Warnings: 0
mysql> select * from models;
+----+--------+
| id | model |
+----+--------+
| 1 | Sports |
| 2 | Sedan |
| 3 | 4WD |
| 4 | Luxury |
+----+--------+
4 rows in set (0.00 sec)
そして最後に、これらすべての他のテーブルを結合するために、すべてを結合するテーブルです。ID フィールドは、実際には車を識別するために使用される一意のロット番号です。
mysql> create table cars (id int(3) not null auto_increment primary key,
-> color int(3), brand int(3), model int(3));
Query OK, 0 rows affected (0.01 sec)
mysql> show columns from cars;
+-------+--------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------+------+-----+---------+----------------+
| id | int(3) | NO | PRI | NULL | auto_increment |
| color | int(3) | YES | | NULL | |
| brand | int(3) | YES | | NULL | |
| model | int(3) | YES | | NULL | |
+-------+--------+------+-----+---------+----------------+
4 rows in set (0.00 sec)
mysql> insert into cars (color, brand, model) values (1,2,1), (3,1,2), (5,3,1),
-> (4,4,2), (2,2,3), (3,5,4), (4,1,3), (2,2,1), (5,2,3), (4,5,1);
Query OK, 10 rows affected (0.00 sec)
Records: 10 Duplicates: 0 Warnings: 0
mysql> select * from cars;
+----+-------+-------+-------+
| id | color | brand | model |
+----+-------+-------+-------+
| 1 | 1 | 2 | 1 |
| 2 | 3 | 1 | 2 |
| 3 | 5 | 3 | 1 |
| 4 | 4 | 4 | 2 |
| 5 | 2 | 2 | 3 |
| 6 | 3 | 5 | 4 |
| 7 | 4 | 1 | 3 |
| 8 | 2 | 2 | 1 |
| 9 | 5 | 2 | 3 |
| 10 | 4 | 5 | 1 |
+----+-------+-------+-------+
10 rows in set (0.00 sec)
これにより、以下のさまざまなタイプの結合の例をカバーするのに十分なデータ (期待) が得られ、それらを価値あるものにするのに十分なデータが得られます。
そのため、上司は自分が持っているすべてのスポーツカーの ID を知りたがっています。
これは単純な 2 つのテーブル結合です。モデルを識別するテーブルと、利用可能な在庫を含むテーブルがあります。ご覧のとおり、テーブルのmodel
列のデータは、テーブルの列にcars
関連しています。これで、models テーブルの ID がfor であることがわかったので、結合を記述します。models
cars
1
Sports
select
ID,
model
from
cars
join models
on model=ID
このクエリは良さそうですよね?2 つのテーブルを識別し、必要な情報を含め、結合する列を正しく識別する結合を使用します。
ERROR 1052 (23000): Column 'ID' in field list is ambiguous
いやいや!最初のクエリでエラーが発生しました! はい、梅です。ご覧のとおり、クエリは実際に正しい列を取得していますが、それらの一部は両方のテーブルに存在するため、データベースは実際の列がどこにあるのか混乱します。これを解決するには2つの解決策があります。1つ目は素晴らしくシンプルで、次tableName.columnName
のように、データベースに正確に何を意味するかを伝えるために使用できます。
select
cars.ID,
models.model
from
cars
join models
on cars.model=models.ID
+----+--------+
| ID | model |
+----+--------+
| 1 | Sports |
| 3 | Sports |
| 8 | Sports |
| 10 | Sports |
| 2 | Sedan |
| 4 | Sedan |
| 5 | 4WD |
| 7 | 4WD |
| 9 | 4WD |
| 6 | Luxury |
+----+--------+
10 rows in set (0.00 sec)
もう 1 つはおそらくより頻繁に使用され、テーブル エイリアシングと呼ばれます。この例のテーブルには短くシンプルな名前が付けられていますが、次のような名前を入力するとKPI_DAILY_SALES_BY_DEPARTMENT
すぐに古くなってしまうため、テーブルに次のようなニックネームを付けるのが簡単な方法です。
select
a.ID,
b.model
from
cars a
join models b
on a.model=b.ID
さて、リクエストに戻ります。ご覧のとおり、必要な情報はありますが、要求されていない情報もあります。そのため、ステートメントに where 句を含めて、要求されたスポーツ カーのみを取得する必要があります。私は、テーブル名を何度も使用するよりも、テーブル エイリアスの方法を好むので、この時点からこの方法に固執します。
明らかに、クエリに where 句を追加する必要があります。スポーツカーはID=1
またはで識別できますmodel='Sports'
。ID はインデックス化されており、主キー (入力が少なくて済みます) であるため、それをクエリで使用してみましょう。
select
a.ID,
b.model
from
cars a
join models b
on a.model=b.ID
where
b.ID=1
+----+--------+
| ID | model |
+----+--------+
| 1 | Sports |
| 3 | Sports |
| 8 | Sports |
| 10 | Sports |
+----+--------+
4 rows in set (0.00 sec)
ビンゴ!上司は幸せです。もちろん、ボスであり、彼が求めたものに決して満足していない.
さて、クエリの大部分は既に作成されていますが、色である 3 番目のテーブルを使用する必要があります。ここで、メインの情報テーブルcars
に車の色 ID が格納され、これが色 ID 列にリンクされます。したがって、元のテーブルと同様の方法で、3 番目のテーブルを結合できます。
select
a.ID,
b.model
from
cars a
join models b
on a.model=b.ID
join colors c
on a.color=c.ID
where
b.ID=1
+----+--------+
| ID | model |
+----+--------+
| 1 | Sports |
| 3 | Sports |
| 8 | Sports |
| 10 | Sports |
+----+--------+
4 rows in set (0.00 sec)
テーブルは正しく結合され、関連する列はリンクされていましたが、リンクしたばかりの新しいテーブルから実際の情報を取得するのを忘れていました。
select
a.ID,
b.model,
c.color
from
cars a
join models b
on a.model=b.ID
join colors c
on a.color=c.ID
where
b.ID=1
+----+--------+-------+
| ID | model | color |
+----+--------+-------+
| 1 | Sports | Red |
| 8 | Sports | Green |
| 10 | Sports | White |
| 3 | Sports | Black |
+----+--------+-------+
4 rows in set (0.00 sec)
そうです、それは私たちの背後にある上司です。ここで、その一部をもう少し詳しく説明します。ご覧のとおりfrom
、ステートメントの句はメイン テーブルにリンクしています (ルックアップ テーブルやディメンション テーブルではなく、情報を含むテーブルをよく使用します。クエリはテーブルをすべて入れ替えても同様に機能しますが、次の場合はあまり意味がありません。数か月後にこのクエリに戻って読むので、多くの場合、適切で理解しやすいクエリを作成することをお勧めします。直感的にレイアウトし、適切なインデントを使用して、すべてが次のように明確になるようにします。他の人に教え続ける場合は、特にトラブルシューティングを行う場合は、クエリにこれらの特性を植え付けるようにしてください。
この方法で、より多くのテーブルをリンクし続けることは完全に可能です。
select
a.ID,
b.model,
c.color
from
cars a
join models b
on a.model=b.ID
join colors c
on a.color=c.ID
join brands d
on a.brand=d.ID
where
b.ID=1
ステートメントに複数の列を結合する可能性があるテーブルを含めるのを忘れていましたがjoin
、ここに例を示します。テーブルにブランド固有のモデルがあり、フィールドのテーブルにリンクさmodels
れたという列もある場合、次のように実行できます。brand
brands
ID
select
a.ID,
b.model,
c.color
from
cars a
join models b
on a.model=b.ID
join colors c
on a.color=c.ID
join brands d
on a.brand=d.ID
and b.brand=d.ID
where
b.ID=1
上記のクエリは、結合されたテーブルをメインcars
テーブルにリンクするだけでなく、既に結合されているテーブル間の結合も指定しています。これが行われなかった場合、結果はデカルト結合と呼ばれます。これは dba の悪口です。デカルト結合は、情報がデータベースに結果を制限する方法を伝えないため、行が返されるものであり、クエリは基準に適合するすべての行を返します。
したがって、デカルト結合の例を示すために、次のクエリを実行してみましょう。
select
a.ID,
b.model
from
cars a
join models b
+----+--------+
| ID | model |
+----+--------+
| 1 | Sports |
| 1 | Sedan |
| 1 | 4WD |
| 1 | Luxury |
| 2 | Sports |
| 2 | Sedan |
| 2 | 4WD |
| 2 | Luxury |
| 3 | Sports |
| 3 | Sedan |
| 3 | 4WD |
| 3 | Luxury |
| 4 | Sports |
| 4 | Sedan |
| 4 | 4WD |
| 4 | Luxury |
| 5 | Sports |
| 5 | Sedan |
| 5 | 4WD |
| 5 | Luxury |
| 6 | Sports |
| 6 | Sedan |
| 6 | 4WD |
| 6 | Luxury |
| 7 | Sports |
| 7 | Sedan |
| 7 | 4WD |
| 7 | Luxury |
| 8 | Sports |
| 8 | Sedan |
| 8 | 4WD |
| 8 | Luxury |
| 9 | Sports |
| 9 | Sedan |
| 9 | 4WD |
| 9 | Luxury |
| 10 | Sports |
| 10 | Sedan |
| 10 | 4WD |
| 10 | Luxury |
+----+--------+
40 rows in set (0.00 sec)
なんてこった、それは醜い。ただし、データベースに関する限り、まさに求められていたものです。クエリでは、ID
fromcars
とmodel
fromを要求しましたmodels
。ただし、テーブルを結合する方法を指定しなかったため、データベースは最初のテーブルのすべての行を 2 番目のテーブルのすべての行と一致させました。
よし、ボスが戻ってきて、またもっと情報を欲しがっている。同じリストが必要ですが、そこに 4WD も含めます。
ただし、これは、これを達成するための 2 つの異なる方法を検討する大きな口実になります。次のように、where 句に別の条件を追加できます。
select
a.ID,
b.model,
c.color
from
cars a
join models b
on a.model=b.ID
join colors c
on a.color=c.ID
join brands d
on a.brand=d.ID
where
b.ID=1
or b.ID=3
上記は完全にうまく機能しますが、別の見方をしてみましょう。これは、union
クエリがどのように機能するかを示す素晴らしい言い訳です。
以下は、すべてのスポーツカーを返すことがわかっています。
select
a.ID,
b.model,
c.color
from
cars a
join models b
on a.model=b.ID
join colors c
on a.color=c.ID
join brands d
on a.brand=d.ID
where
b.ID=1
以下は、すべての 4WD を返します。
select
a.ID,
b.model,
c.color
from
cars a
join models b
on a.model=b.ID
join colors c
on a.color=c.ID
join brands d
on a.brand=d.ID
where
b.ID=3
したがって、union all
それらの間に句を追加すると、2 番目のクエリの結果が最初のクエリの結果に追加されます。
select
a.ID,
b.model,
c.color
from
cars a
join models b
on a.model=b.ID
join colors c
on a.color=c.ID
join brands d
on a.brand=d.ID
where
b.ID=1
union all
select
a.ID,
b.model,
c.color
from
cars a
join models b
on a.model=b.ID
join colors c
on a.color=c.ID
join brands d
on a.brand=d.ID
where
b.ID=3
+----+--------+-------+
| ID | model | color |
+----+--------+-------+
| 1 | Sports | Red |
| 8 | Sports | Green |
| 10 | Sports | White |
| 3 | Sports | Black |
| 5 | 4WD | Green |
| 7 | 4WD | White |
| 9 | 4WD | Black |
+----+--------+-------+
7 rows in set (0.00 sec)
ご覧のとおり、最初のクエリの結果が最初に返され、次に 2 番目のクエリの結果が返されます。
この例では、最初のクエリを単純に使用する方がはるかに簡単union
ですが、特定のケースではクエリが優れている場合があります。これらは、簡単に結合できないテーブルのテーブルから特定の結果を返す優れた方法です。さらに言えば、完全に無関係なテーブルです。ただし、従うべきいくつかの規則があります。
- 最初のクエリの列の型は、以下の他のすべてのクエリの列の型と一致する必要があります。
- 最初のクエリの列の名前は、結果のセット全体を識別するために使用されます。
- 各クエリの列数は同じでなければなりません。
ここで、 と の使用の違いについて疑問に思うかもしれません。クエリは重複を削除しますが、 は削除しません。これは、 overを使用するとパフォーマンスがわずかに低下することを意味しますが、結果はそれに値する可能性があります。ただし、この種のことについては推測しません。union
union all
union
union all
union
union all
この点について、ここでいくつかの追加の注記に注意する価値があるかもしれません。
- 結果を並べ替えたい場合は、 an を使用でき
order by
ますが、エイリアスは使用できなくなりました。上記のクエリでは、 を追加するorder by a.ID
とエラーが発生します。結果に関する限り、同じエイリアスが両方のクエリで使用されていても、列がID
呼び出されます。a.ID
- ステートメントは 1 つしか持てず
order by
、最後のステートメントでなければなりません。
次の例では、いくつかの行をテーブルに追加しています。
Holden
ブランド表に追加しました。色テーブルに参照がない -の値cars
を持つ行も追加しました。color
12
オーケー、上司がまた戻ってきて、リクエストを吠えています - *私たちが扱っている各ブランドの数とそれに含まれる車の数が欲しい!」 - 通常、私たちは議論の興味深いセクションにたどり着き、上司はもっと仕事を求めています.
したがって、最初に行う必要があるのは、可能なブランドの完全なリストを取得することです.
select
a.brand
from
brands a
+--------+
| brand |
+--------+
| Ford |
| Toyota |
| Nissan |
| Smart |
| BMW |
| Holden |
+--------+
6 rows in set (0.00 sec)
これを cars テーブルに結合すると、次の結果が得られます。
select
a.brand
from
brands a
join cars b
on a.ID=b.brand
group by
a.brand
+--------+
| brand |
+--------+
| BMW |
| Ford |
| Nissan |
| Smart |
| Toyota |
+--------+
5 rows in set (0.00 sec)
Holden
もちろん、これは問題です。私が追加した素敵なブランドについて言及されていません。
これは、結合が両方のテーブルで一致する行を探すためです。タイプの車にはデータがHolden
ないため、返されません。ここでouter
結合を使用できます。これにより、他のテーブルで一致するかどうかに関係なく、1 つのテーブルからすべての結果が返されます。
select
a.brand
from
brands a
left outer join cars b
on a.ID=b.brand
group by
a.brand
+--------+
| brand |
+--------+
| BMW |
| Ford |
| Holden |
| Nissan |
| Smart |
| Toyota |
+--------+
6 rows in set (0.00 sec)
これで、素敵な集計関数を追加してカウントを取得し、ボスを少しの間背負わせることができます。
select
a.brand,
count(b.id) as countOfBrand
from
brands a
left outer join cars b
on a.ID=b.brand
group by
a.brand
+--------+--------------+
| brand | countOfBrand |
+--------+--------------+
| BMW | 2 |
| Ford | 2 |
| Holden | 0 |
| Nissan | 1 |
| Smart | 1 |
| Toyota | 5 |
+--------+--------------+
6 rows in set (0.00 sec)
それで、ボスのスカルクを遠ざけます。
ここで、これをもう少し詳しく説明すると、外部結合はleft
orright
型にすることができます。Left または Right は、どのテーブルが完全に含まれるかを定義します。Aleft outer join
は左側のテーブルのすべての行を含み、(ご想像のとおり) aright outer join
は右側のテーブルのすべての結果を結果に取り込みます。
一部のデータベースでは、両方のfull outer join
テーブルから (一致するかどうかにかかわらず) 結果を返すが許可されますが、これはすべてのデータベースでサポートされているわけではありません。
さて、おそらく現時点では、クエリで結合タイプをマージできるかどうか疑問に思っていると思いますが、答えはイエスです。絶対に可能です。
select
b.brand,
c.color,
count(a.id) as countOfBrand
from
cars a
right outer join brands b
on b.ID=a.brand
join colors c
on a.color=c.ID
group by
a.brand,
c.color
+--------+-------+--------------+
| brand | color | countOfBrand |
+--------+-------+--------------+
| Ford | Blue | 1 |
| Ford | White | 1 |
| Toyota | Black | 1 |
| Toyota | Green | 2 |
| Toyota | Red | 1 |
| Nissan | Black | 1 |
| Smart | White | 1 |
| BMW | Blue | 1 |
| BMW | White | 1 |
+--------+-------+--------------+
9 rows in set (0.00 sec)
では、なぜそれが期待された結果ではないのでしょうか? これは、車からブランドへの外部結合を選択しましたが、色への結合で指定されていないためです。特定の結合では、両方のテーブルで一致する結果のみが返されます。
期待どおりの結果を得るために機能するクエリを次に示します。
select
a.brand,
c.color,
count(b.id) as countOfBrand
from
brands a
left outer join cars b
on a.ID=b.brand
left outer join colors c
on b.color=c.ID
group by
a.brand,
c.color
+--------+-------+--------------+
| brand | color | countOfBrand |
+--------+-------+--------------+
| BMW | Blue | 1 |
| BMW | White | 1 |
| Ford | Blue | 1 |
| Ford | White | 1 |
| Holden | NULL | 0 |
| Nissan | Black | 1 |
| Smart | White | 1 |
| Toyota | NULL | 1 |
| Toyota | Black | 1 |
| Toyota | Green | 2 |
| Toyota | Red | 1 |
+--------+-------+--------------+
11 rows in set (0.00 sec)
ご覧のとおり、クエリには 2 つの外部結合があり、結果は期待どおりになっています。
さて、あなたが求める他のタイプの結合はどうですか? 交差点はどうですか?
すべてのデータベースが をサポートしているわけではありませんintersection
が、ほぼすべてのデータベースで、結合 (または少なくとも適切に構造化された where ステートメント) を使用して交差を作成できます。
union
Intersection は、前述の a に多少似た結合のタイプですが、違いは、結合によって結合されたさまざまな個々のクエリ間で同一の (つまり同一という意味です) データの行のみを返すことです。すべての点で同一の行のみが返されます。
簡単な例は次のようになります。
select
*
from
colors
where
ID>2
intersect
select
*
from
colors
where
id<4
通常のunion
クエリではテーブルのすべての行が返されますが (最初のクエリは を超えるものを返しID>2
、2 番目のクエリは を持つものを返しID<4
ます)、完全なセットになりますが、交差クエリid=3
は両方の基準を満たすため、一致する行のみを返します。
データベースがクエリをサポートしていない場合intersect
、上記は次のクエリで簡単に達成できます。
select
a.ID,
a.color,
a.paint
from
colors a
join colors b
on a.ID=b.ID
where
a.ID>2
and b.ID<4
+----+-------+----------+
| ID | color | paint |
+----+-------+----------+
| 3 | Blue | Metallic |
+----+-------+----------+
1 row in set (0.00 sec)
交差クエリを本質的にサポートしていないデータベースを使用して、2 つの異なるテーブル間で交差を実行する場合は、テーブルのすべての列で結合を作成する必要があります。