SQL で重複排除をしたいときに DISTINCT
と GROUP BY
を使う例をみかけます.この2つは何が違うのでしょうか?
準備
検証用に以下のようなテーブルを用意します.
mysql> SELECT * FROM test;
+----+------+------+------+
| id | a | b | c |
+----+------+------+------+
| 1 | 1 | 10 | 100 |
| 2 | 1 | 10 | 200 |
| 3 | 2 | 10 | 300 |
| 4 | 2 | 20 | 400 |
| 5 | 3 | 30 | 500 |
+----+------+------+------+
GROUP BY は何をしているのか?
GROUP BY
は
- 指定された列名(属性)で行をグループ化し,
- 各グループから 1行取り出し,
- 取り出した行を集めてテーブルを再構成する.
という処理をします.
実際の動作を見てみましょう.
mysql> SELECT * FROM test GROUP BY a ;
+----+------+------+------+
| id | a | b | c |
+----+------+------+------+
| 1 | 1 | 10 | 100 |
| 3 | 2 | 10 | 300 |
| 5 | 3 | 30 | 500 |
+----+------+------+------+
この SQL は, MySQL 5.6 以前でないと結果が表示されません.
そもそも,グループ化を指定した列以外の列も SELECT で表示させるのは,意味がないですね.
詳しくは ONLY_FULL_GROUP_BY
でググってみてください,
ですが,これをみると, GROUP BY
の処理がわかります.
まず, GROUP BY a
により, a の値で,以下のようにグループ化されます.
+----+------+------+------+
| id | a | b | c |
+----+------+------+------+
| 1 | 1 | 10 | 100 |
| 2 | 1 | 10 | 200 |
+----+------+------+------+
+----+------+------+------+
| id | a | b | c |
+----+------+------+------+
| 3 | 2 | 10 | 300 |
| 4 | 2 | 20 | 400 |
+----+------+------+------+
+----+------+------+------+
| id | a | b | c |
+----+------+------+------+
| 5 | 3 | 30 | 500 |
+----+------+------+------+
次に,各グループから,1行取り出します.ここでは,最初の行を取り出すことにします.
+----+------+------+------+
| id | a | b | c |
+----+------+------+------+
| 1 | 1 | 10 | 100 |
+----+------+------+------+
+----+------+------+------+
| id | a | b | c |
+----+------+------+------+
| 3 | 2 | 10 | 300 |
+----+------+------+------+
+----+------+------+------+
| id | a | b | c |
+----+------+------+------+
| 5 | 3 | 30 | 500 |
+----+------+------+------+
最後に,各々のグループから取り出した列を集めて,テーブルを再構成します.
これで,先程の実行結果になります.
+----+------+------+------+
| id | a | b | c |
+----+------+------+------+
| 1 | 1 | 10 | 100 |
| 3 | 2 | 10 | 300 |
| 5 | 3 | 30 | 500 |
+----+------+------+------+
集約関数(COUNT など) が使用されると,これらの取り出した列に集約関数の結果の列が追加されます.
mysql> SELECT *, COUNT(*) AS cnt FROM test GROUP BY a ;
+----+------+------+------+-----+
| id | a | b | c | cnt |
+----+------+------+------+-----+
| 1 | 1 | 10 | 100 | 2 |
| 3 | 2 | 10 | 300 | 2 |
| 5 | 3 | 30 | 500 | 1 |
+----+------+------+------+-----+
DISTINCT は何をしているのか?
先の実行例で, SELECT
で最終的に表示すべき列を指定すると,その列だけが表示されます.
mysql> SELECT a, COUNT(*) AS cnt FROM test GROUP BY a;
+------+-----+
| a | cnt |
+------+-----+
| 1 | 2 |
| 2 | 2 |
| 3 | 1 |
+------+-----+
ここで, SELECT
が指定しているのは
+----+------+------+------+-----+
| id | a | b | c | cnt |
+----+------+------+------+-----+
から,出力するテーブルに残す列です. これは,関係演算の射影に対する指定になります.
つまり, GROUP BY a
と,出力の a には,厳密には関係性がないことになります.
ですので,
mysql> SELECT COUNT(*) AS cnt FROM test GROUP BY a;
+-----+
| cnt |
+-----+
| 2 |
| 2 |
| 1 |
+-----+
とすることもできますね.
さて,ここで, DISTINCT
です.
DISTINCT
は,射影の過程で重複を排除します.
ですので,上記の例に DISTINCT
をつけると
mysql> SELECT DISTINCT COUNT(*) AS cnt FROM test GROUP BY a;
+-----+
| cnt |
+-----+
| 2 |
| 1 |
+-----+
のように, 2
の重複が排除されてしまいます.
まとめ
GROUP BY
は
- 指定された列名(属性)で行をグループ化し,
- 各グループから 1行取り出し,
- 取り出した行を集めてテーブルを再構成する.
を行う.
DISTINCT
は,射影の過程で重複を排除する.
でした.
つまり,
- SQL を処理する過程で適用される場所がちがう
-
GROUP BY
は結果的に重複排除になるが,本来はグループ化を行っている(当たり前ですね)
ということになります.