75
51

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-12-15

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

ということになります.

75
51
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
75
51

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?