Help us understand the problem. What is going on with this article?

SQL: DISTINCT と GROUP BY って,何がちがうの?

More than 1 year has passed since last update.

SQL で重複排除をしたいときに DISTINCTGROUP 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 は結果的に重複排除になるが,本来はグループ化を行っている(当たり前ですね)

ということになります.

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした