【SQL】結合によって結合元テーブルの行数が増えるケースについて

質問サイトを巡回中に発見

LEFT JOINを実行した際に、NULLの値を持つレコードが増えるケースについては認知していましたが、ただのJOINの時にもレコード数が増えることを経験しました。 SQLに詳しい方から見れば、当たり前でしょと思われるかもしれませんが、遭遇したのが初めて、例を見かけなかったので記事にしてみました。

前提条件

以下2つのテーブルがあります。subjectは別テーブルにしようかと思いましたが、今回は簡単のためexam_resultVARCHAR(20)として定義しています。

学生情報(student)

                       Table "public.student"
   Column   |         Type          | Collation | Nullable | Default 
------------+-----------------------+-----------+----------+---------
 student_id | integer               |           | not null | 
 name       | character varying(50) |           | not null | 
 grade      | integer               |           | not null | 
 gendar     | character(1)          |           |          | 

試験結果(exam_result)

                       Table "public.exam_result"
     Column     |         Type          | Collation | Nullable | Default 
----------------+-----------------------+-----------+----------+---------
 exam_result_id | integer               |           | not null | 
 student_id     | integer               |           | not null | 
 subject        | character varying(20) |           | not null | 
 score          | integer               |           | not null | 

それぞれサンプルデータを用意しています。

postgres=# SELECT * FROM student;
 student_id |    name    | grade | gendar 
------------+------------+-------+--------
          1 | 岡部倫太郎 |     2 | 1
          2 | 椎名まゆり |     1 | 2
(2 rows)
postgres=# SELECT * FROM exam_result;
 exam_result_id | student_id | subject | score 
----------------+------------+---------+-------
              1 |          1 | 国語    |    34
              2 |          1 | 数学    |    87
              3 |          1 | 化学    |    75
              4 |          2 | 国語    |    81
(4 rows)

結合してみると...

JOINを使って結合をした結果が以下です。元々、studentテーブルにはレコードが2件しかないのに結合結果には4件のレコード数が表示されています。

postgres=# SELECT * FROM student JOIN exam_result ON student.student_id = exam_result.student_id;
 student_id |    name    | grade | gendar | exam_result_id | student_id | subject | score 
------------+------------+-------+--------+----------------+------------+---------+-------
          1 | 岡部倫太郎 |     2 | 1      |              1 |          1 | 国語    |    34
          1 | 岡部倫太郎 |     2 | 1      |              2 |          1 | 数学    |    87
          1 | 岡部倫太郎 |     2 | 1      |              3 |          1 | 化学    |    75
          2 | 椎名まゆり |     1 | 2      |              4 |          2 | 国語    |    81
(4 rows)
postgres=# SELECT COUNT(student_id) FROM student;
 count 
-------
     2

LEFT JOINの場合はどうでしょうか。こちらは想定通り結合後の4件が出力されました。

postgres=# SELECT * FROM student LEFT JOIN exam_result ON student.student_id = exam_result.student_id;
 student_id |    name    | grade | gendar | exam_result_id | student_id | subject | score 
------------+------------+-------+--------+----------------+------------+---------+-------
          1 | 岡部倫太郎 |     2 | 1      |              1 |          1 | 国語    |    34
          1 | 岡部倫太郎 |     2 | 1      |              2 |          1 | 数学    |    87
          1 | 岡部倫太郎 |     2 | 1      |              3 |          1 | 化学    |    75
          2 | 椎名まゆり |     1 | 2      |              4 |          2 | 国語    |    81
(4 rows)

なぜ行数が増えるのか

理由は結合に用いているkeyが重複しているからです。今回の例で言うとstudentへの結合条件に指定しているstudent_idexam_resultテーブルで重複しています。これは岡部倫太郎が複数科目で試験を受けているためです。

student_id=1: 岡部倫太郎
student_id=1の試験結果: 国語, 数学, 化学

処理結果としては正常な動きですが、結合先に結合に用いたkeyの値が重複するようなケースは注意が必要になります。 というか、結合元のテーブルの行数が増えた!と捉えることが間違いで、結合によって新しく作られたテーブルには行数が4件ある条件が指定されていたと考える方が適切でしょう。