SQL: DISTINCT と GROUP BY って，何がちがうの？

 SQL で重複排除をしたいときに `DISTINCT` と `GROUP BY` を使う例をみかけます．この２つは何が違うのでしょうか？

## 準備

　検証用に以下のようなテーブルを用意します．

```sql
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行取り出し，
* 取り出した行を集めてテーブルを再構成する．

という処理をします．

　実際の動作を見てみましょう．

```sql
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 の値で，以下のようにグループ化されます．

```sql
+----+------+------+------+
| id | a    | b    | c    |
+----+------+------+------+
|  1 |    1 |   10 |  100 |
|  2 |    1 |   10 |  200 |
+----+------+------+------+
```

```sql
+----+------+------+------+
| id | a    | b    | c    |
+----+------+------+------+
|  3 |    2 |   10 |  300 |
|  4 |    2 |   20 |  400 |
+----+------+------+------+
```

```sql
+----+------+------+------+
| id | a    | b    | c    |
+----+------+------+------+
|  5 |    3 |   30 |  500 |
+----+------+------+------+
```

次に，各グループから，１行取り出します．ここでは，最初の行を取り出すことにします．

```sql
+----+------+------+------+
| id | a    | b    | c    |
+----+------+------+------+
|  1 |    1 |   10 |  100 |
+----+------+------+------+
```

```sql
+----+------+------+------+
| id | a    | b    | c    |
+----+------+------+------+
|  3 |    2 |   10 |  300 |
+----+------+------+------+
```

```sql
+----+------+------+------+
| id | a    | b    | c    |
+----+------+------+------+
|  5 |    3 |   30 |  500 |
+----+------+------+------+
```

　最後に，各々のグループから取り出した列を集めて，テーブルを再構成します．
　これで，先程の実行結果になります．

```sql
+----+------+------+------+
| id | a    | b    | c    |
+----+------+------+------+
|  1 |    1 |   10 |  100 |
|  3 |    2 |   10 |  300 |
|  5 |    3 |   30 |  500 |
+----+------+------+------+
```

　集約関数(COUNT など) が使用されると，これらの取り出した列に集約関数の結果の列が追加されます．

```sql
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` で最終的に表示すべき列を指定すると，その列だけが表示されます．

```sql
mysql> SELECT a, COUNT(*) AS cnt FROM test GROUP BY a;
+------+-----+
| a    | cnt |
+------+-----+
|    1 |   2 |
|    2 |   2 |
|    3 |   1 |
+------+-----+
```

　ここで， `SELECT` が指定しているのは

```sql
+----+------+------+------+-----+
| id | a    | b    | c    | cnt |
+----+------+------+------+-----+
```

から，出力するテーブルに残す列です．　これは，関係演算の射影に対する指定になります．
　つまり， `GROUP BY a` と，出力の a には，厳密には関係性がないことになります．
　ですので，

```sql
mysql> SELECT COUNT(*) AS cnt FROM test GROUP BY a;
+-----+
| cnt |
+-----+
|   2 |
|   2 |
|   1 |
+-----+
```

とすることもできますね．

　さて，ここで， `DISTINCT`　です．
　 `DISTINCT` は，**射影の過程で重複を排除**します．
 ですので，上記の例に `DISTINCT` をつけると

```sql
mysql> SELECT DISTINCT COUNT(*) AS cnt FROM test GROUP BY a;
+-----+
| cnt |
+-----+
|   2 |
|   1 |
+-----+
```

のように， `2` の重複が排除されてしまいます．


## まとめ

`GROUP BY` は

* 指定された列名（属性）で行をグループ化し，
* 各グループから 1行取り出し，
* 取り出した行を集めてテーブルを再構成する．

を行う．

　 `DISTINCT` は，**射影の過程で重複を排除**する．

でした．

　つまり，

* SQL を処理する過程で適用される場所がちがう
* `GROUP BY` は結果的に重複排除になるが，本来はグループ化を行っている（当たり前ですね）

ということになります．