準備
以下の2つのテーブルを準備する。
- students
- 学生IDと学生名を保存。
- courses
- 学科IDと学科名を保存。
students
テーブルの作成とデータ登録のクエリー。
1 2 3 4 5 6 7 8 9 10 |
CREATE TABLE students ( id INT, name VARCHAR(10) ); INSERT INTO students VALUES (1, '安藤'), (2, '伊藤'), (3, '宇藤'), (4, '江藤'); |
実行結果。
1 2 3 4 5 6 7 8 9 10 |
mysql> SELECT * FROM students; +------+--------+ | id | name | +------+--------+ | 1 | 安藤 | | 2 | 伊藤 | | 3 | 宇藤 | | 4 | 江藤 | +------+--------+ 4 rows in set (0.00 sec) |
courses
テーブルの作成とデータ登録のクエリー。
1 2 3 4 5 6 7 8 9 10 |
CREATE TABLE courses ( id INT, name VARCHAR(10) ); INSERT INTO courses VALUES (10, '数学'), (11, '物理学'), (12, '化学'), (13, '天文学'); |
実行結果。
1 2 3 4 5 6 7 8 9 10 |
mysql> SELECT * FROM courses; +------+-----------+ | id | name | +------+-----------+ | 10 | 数学 | | 11 | 物理学 | | 12 | 化学 | | 13 | 天文学 | +------+-----------+ 4 rows in set (0.00 sec) |
JOIN~総当たり
FROM句のテーブルに対して単にJOINでテーブルを指定すると、2つのテーブルの全要素の組み合わせが得られる。
1 2 3 |
SELECT students.name, courses.name FROM students JOIN courses; |
実行結果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
+--------+-----------+ | name | name | +--------+-----------+ | 江藤 | 数学 | | 宇藤 | 数学 | | 伊藤 | 数学 | | 安藤 | 数学 | | 江藤 | 物理学 | | 宇藤 | 物理学 | | 伊藤 | 物理学 | | 安藤 | 物理学 | | 江藤 | 化学 | | 宇藤 | 化学 | | 伊藤 | 化学 | | 安藤 | 化学 | | 江藤 | 天文学 | | 宇藤 | 天文学 | | 伊藤 | 天文学 | | 安藤 | 天文学 | +--------+-----------+ 16 rows in set (0.00 sec) |
結合テーブル
students
テーブルとcourses
テーブルの結合テーブルを準備する。各学生の履修学科に関するデータ。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
CREATE TABLE student_courses ( student_id INT, course_id INT ); INSERT INTO student_courses VALUES (1, 10), (1, 11), (3, 10), (3, 11), (3, 13), (4, 11), (4, 13); |
クエリーの実行結果、
1 2 3 4 5 6 7 8 9 10 11 12 13 |
mysql> SELECT * FROM student_courses; +------------+-----------+ | student_id | course_id | +------------+-----------+ | 1 | 10 | | 1 | 11 | | 3 | 10 | | 3 | 11 | | 3 | 13 | | 4 | 11 | | 4 | 13 | +------------+-----------+ 7 rows in set (0.00 sec) |
INNER JOIN
結合テーブルに基づいて、2つのテーブルの両方に存在するデータを取り出す。students
とcourses
の積集合に相当する。INNER
は書かなくても内部結合になる。
1 2 3 4 5 6 7 |
SELECT カラム1, カラム2, ..... FROM テーブル1 [AS エイリアス1] [INNER] JOIN テーブル2 [AS エイリアス2] ON [テーブル1].キー1 = [テーブル2].キー2; |
student_courses
テーブルとstudents
テーブルを結合するクエリーの例。結合の結果、students
テーブルから学生名を得られる。
1 2 3 4 5 6 7 |
SELECT stcs.student_id, st.name, stcs.course_id FROM student_courses AS stcs JOIN students AS st ON stcs.student_id = st.id; |
実行結果。
1 2 3 4 5 6 7 8 9 10 11 12 |
+------------+--------+-----------+ | student_id | name | course_id | +------------+--------+-----------+ | 1 | 安藤 | 10 | | 1 | 安藤 | 11 | | 3 | 宇藤 | 10 | | 3 | 宇藤 | 11 | | 3 | 宇藤 | 13 | | 4 | 江藤 | 11 | | 4 | 江藤 | 13 | +------------+--------+-----------+ 7 rows in set (0.00 sec) |
3つのテーブルの場合、JOIN ... ON
句を並べればよい。以下の例ではcourses
テーブルも結合して、学科名も得ている。
1 2 3 4 5 6 7 8 9 10 |
SELECT stcs.student_id, st.name AS student_name, stcs.course_id, cs.name AS course_name FROM student_courses AS stcs JOIN students AS st ON stcs.student_id = st.id JOIN courses AS cs ON stcs.course_id = cs.id; |
実行結果。student_courses
テーブルは学生IDと学科IDしか持っていないが、students
テーブル、courses
テーブルと結合することで学生名と学科名を使えるようになる。
1 2 3 4 5 6 7 8 9 10 11 12 |
+------------+--------------+-----------+-------------+ | student_id | student_name | course_id | course_name | +------------+--------------+-----------+-------------+ | 3 | 宇藤 | 10 | 数学 | | 1 | 安藤 | 10 | 数学 | | 4 | 江藤 | 11 | 物理学 | | 3 | 宇藤 | 11 | 物理学 | | 1 | 安藤 | 11 | 物理学 | | 4 | 江藤 | 13 | 天文学 | | 3 | 宇藤 | 13 | 天文学 | +------------+--------------+-----------+-------------+ 7 rows in set (0.00 sec) |
LEFT OUTER JOIN
LEFT OUTER JOIN
は、先に指定されたテーブル(左テーブル)の全レコードに対して、JOIN
で指定したレコードを対応させる。左テーブルに対応するレコードがないときはNULL
になる。LEFT JOIN
と書いても同じ。
以下のクエリーは、students
の全学生データに対してstudent_courses
の履修科目データを左結合させる。students
には登録されているがstudent_courses
にない学生(学科を履修していない学生)についてはNULL
になる。
1 2 3 4 5 6 7 |
SELECT st.id AS student_id, st.name, stcs.course_id FROM students AS st LEFT JOIN student_courses AS stcs ON st.id = stcs.student_id; |
実行結果は以下の通りで、学生IDの伊藤君はstudent_courses
テーブルに存在しないので、course_id
がNULL
になっている。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
+------------+--------+-----------+ | student_id | name | course_id | +------------+--------+-----------+ | 1 | 安藤 | 11 | | 1 | 安藤 | 10 | | 2 | 伊藤 | NULL | | 3 | 宇藤 | 13 | | 3 | 宇藤 | 11 | | 3 | 宇藤 | 10 | | 4 | 江藤 | 13 | | 4 | 江藤 | 11 | +------------+--------+-----------+ 8 rows in set (0.00 sec) |
学科名も欲しい場合は、courses
テーブルを左結合させる。
1 2 3 4 5 6 7 8 9 10 |
SELECT st.id AS student_id, st.name AS student_name, cs.id AS course_id, cs.name AS course_name FROM students AS st LEFT JOIN student_courses AS stcs ON st.id = stcs.student_id LEFT JOIN courses AS cs ON stcs.course_id = cs.id; |
実行結果。学科を履修していない伊藤君のデータは、course_name
についてもNULL
になる。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
+------------+--------------+-----------+-------------+ | student_id | student_name | course_id | course_name | +------------+--------------+-----------+-------------+ | 1 | 安藤 | 11 | 物理学 | | 1 | 安藤 | 10 | 数学 | | 2 | 伊藤 | NULL | NULL | | 3 | 宇藤 | 13 | 天文学 | | 3 | 宇藤 | 11 | 物理学 | | 3 | 宇藤 | 10 | 数学 | | 4 | 江藤 | 13 | 天文学 | | 4 | 江藤 | 11 | 物理学 | +------------+--------------+-----------+-------------+ 8 rows in set (0.00 sec) |
RIGHT OUTER JOIN
RIGHT JOIN
は後から指定されたテーブルの全データに対して、先に指定されたテーブルを結合させる。先に指定されたテーブルに対応するレコードがない場合にはNULL
になる。RIGHT JOIN
と書いても同じ。
以下は、student_courses
テーブルのデータにcourses
テーブルのデータを右結合させている。どの学生にも履修されていない学科がある場合はNULL
になる。
1 2 3 4 5 6 7 |
SELECT stcs.student_id, cs.id AS course_id, cs.name AS course_name FROM student_courses AS stcs RIGHT JOIN courses as cs ON stcs.course_id = cs.id; |
実行結果。化学はどの学生にも履修されていないのでNULL
になっている。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
+------------+-----------+-------------+ | student_id | course_id | course_name | +------------+-----------+-------------+ | 3 | 10 | 数学 | | 1 | 10 | 数学 | | 4 | 11 | 物理学 | | 3 | 11 | 物理学 | | 1 | 11 | 物理学 | | NULL | 12 | 化学 | | 4 | 13 | 天文学 | | 3 | 13 | 天文学 | +------------+-----------+-------------+ 8 rows in set (0.00 sec) |
3つ以上のテーブルでは、最も右側のテーブルをRIGHT JOIN
を連ねる。最後以外のJOIN
はRIGHT
がなくても同じ結果になる。
1 2 3 4 5 6 7 8 9 10 |
SELECT st.id AS student_id, st.name AS student_name, cs.id AS course_id, cs.name AS course_name FROM student_courses AS stcs RIGHT JOIN students AS st ON stcs.student_id = st.id RIGHT JOIN courses as cs ON stcs.course_id = cs.id; |
実行結果。履修されていない化学の学生IDと学生名がNULL
になっている。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
+------------+--------------+-----------+-------------+ | student_id | student_name | course_id | course_name | +------------+--------------+-----------+-------------+ | 3 | 宇藤 | 10 | 数学 | | 1 | 安藤 | 10 | 数学 | | 4 | 江藤 | 11 | 物理学 | | 3 | 宇藤 | 11 | 物理学 | | 1 | 安藤 | 11 | 物理学 | | NULL | NULL | 12 | 化学 | | 4 | 江藤 | 13 | 天文学 | | 3 | 宇藤 | 13 | 天文学 | +------------+--------------+-----------+-------------+ 8 rows in set (0.00 sec) |
RIGHT JOIN
のテーブルを入れ替えることで、LEFT JOIN
で同じ結果を得られる。履修科目に関する上の例をLEFT JOIN
で書きなおすと以下の通り。
1 2 3 4 5 6 7 8 9 10 |
SELECT st.id AS student_id, st.name AS student_name, cs.id AS course_id, cs.name AS course_name FROM courses AS cs LEFT JOIN student_courses AS stcs ON stcs.course_id = cs.id LEFT JOIN students AS st ON stcs.student_id = st.id; |
実行結果は同じ。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
+------------+--------------+-----------+-------------+ | student_id | student_name | course_id | course_name | +------------+--------------+-----------+-------------+ | 3 | 宇藤 | 10 | 数学 | | 1 | 安藤 | 10 | 数学 | | 4 | 江藤 | 11 | 物理学 | | 3 | 宇藤 | 11 | 物理学 | | 1 | 安藤 | 11 | 物理学 | | NULL | NULL | 12 | 化学 | | 4 | 江藤 | 13 | 天文学 | | 3 | 宇藤 | 13 | 天文学 | +------------+--------------+-----------+-------------+ 8 rows in set (0.00 sec) |
FULL OUTER JOIN
FULL OUTER JOIN
は指定されたテーブルの全データを含める。いずれかのデータが存在しない場合はNULL
となる。テーブルのデータ群の和集合に相当する。
MySQLにはFULL JOIN
が定義されていないので、同等の機能はUNION
で実現する。
- 2つのテーブルの場合には
LEFT JOIN
とRIGHT JOIN
の結果をUNION
- 結合テーブルがある場合には、それぞれの
LEFT JOIN
の結果をUNION
UNION
の際に、各SELECT
のカラムを同じにしておく
以下は、students
、courses
に対するstudent_courses
のLEFT JOIN
の結果をUNION
で結合している。科目を履修していない学生は学科がNULL
となり、履修されていない科目が学生がNULL
となる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
SELECT st.id AS student_id, st.name AS student_name, cs.id AS course_id, cs.name AS course_name FROM students AS st LEFT JOIN student_courses AS stcs ON st.id = stcs.student_id LEFT JOIN courses AS cs ON stcs.course_id = cs.id UNION SELECT st.id AS student_id, st.name AS student_name, cs.id AS course_id, cs.name AS course_name FROM courses AS cs LEFT JOIN student_courses AS stcs ON stcs.course_id = cs.id LEFT JOIN students AS st ON stcs.student_id = st.id; |
実行結果。学科を履修していない伊藤君の学科欄はNULL
になり、だれにも履修されていない化学は学生欄がNULL
になっている。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
+------------+--------------+-----------+-------------+ | student_id | student_name | course_id | course_name | +------------+--------------+-----------+-------------+ | 1 | 安藤 | 11 | 物理学 | | 1 | 安藤 | 10 | 数学 | | 2 | 伊藤 | NULL | NULL | | 3 | 宇藤 | 13 | 天文学 | | 3 | 宇藤 | 11 | 物理学 | | 3 | 宇藤 | 10 | 数学 | | 4 | 江藤 | 13 | 天文学 | | 4 | 江藤 | 11 | 物理学 | | NULL | NULL | 12 | 化学 | +------------+--------------+-----------+-------------+ 9 rows in set (0.00 sec) |
カラム名の指定によって結果が違う
LEFT OUTER JOIN
のところで、学生IDと学科IDをstudents.id
、couses.id
のようにそれぞれのテーブルから指定していた。
一方で、同じIDをstudent_courseのstudent_id
、couse_id
で指定することもできる。こちらで指定すれば、識別のためのエイリアスを定義しなくていいので便利そうだ。
1 2 3 4 5 6 7 8 9 10 |
SELECT stcs.student_id, st.name AS student_name, stcs.course_id, cs.name AS course_name FROM courses AS cs LEFT JOIN student_courses AS stcs ON stcs.course_id = cs.id LEFT JOIN students AS st ON stcs.student_id = st.id; |
しかしこれらを使うと、OUTER JOIN
の際に存在しないIDとして扱われてNULL
になってしまう。
上のクエリーを実行した結果が以下で、化学を履修している学生がいないので、student_courses
に該当するデータがなく、student_id
とcourse_id
がNULL
になっている。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
+------------+--------------+-----------+-------------+ | student_id | student_name | course_id | course_name | +------------+--------------+-----------+-------------+ | 3 | 宇藤 | 10 | 数学 | | 1 | 安藤 | 10 | 数学 | | 4 | 江藤 | 11 | 物理学 | | 3 | 宇藤 | 11 | 物理学 | | 1 | 安藤 | 11 | 物理学 | | NULL | NULL | NULL | 化学 | | 4 | 江藤 | 13 | 天文学 | | 3 | 宇藤 | 13 | 天文学 | +------------+--------------+-----------+-------------+ 8 rows in set (0.00 sec) |
これらを表示させたいときには、LEFT OUTER JOIN
の例のように、左結合される側(全データが使われる側)のテーブルでカラムを指定するとよい。