【エンジニア教材】MySQL編(4) GROUP BY,HAVINGについて

# 【エンジニア教材】MySQL編（4）GROUP BY,HAVING


## まえがき

このドキュメントは社員やインターン生を教育するために作成したものですが、
ぜひ社外の人にも見ていただきたいと思い公開いたしました。

ちょこちょこ更新しますのでリクエストなどございましたらお気軽にお申し付け下さい。
体系的に書き進めていくのでリクエストしていただいたものについてすぐかける保証はございませんのでご理解とご了承のほどよろしくお願いいたします。
もし役に立ったら、いいね！やストックをしてもらえると励みになりますm(_ _)m

ちなみにプロフィールはこちら。
https://www.wantedly.com/users/1955894

## SELECT文で集合関数を理解する。

今回はSELECT文で集合関数を使ってレコードを集計するということをやって見ます。
はじめにデータを用意します。
今回はテスト用のデータをランダムに生成してくれるこちらのサイトを用いてデータをインポートしました。
http://kazina.com/dummy/index.html

同じデータを用いて試して見たいという方は次のSQLを実行してデータを入れて見てください。

```
CREATE TABLE `users` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(100) DEFAULT NULL,
  `name_kana` varchar(100) DEFAULT NULL,
  `email` varchar(100) DEFAULT NULL,
  `gender` varchar(10) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `blood_type` varchar(10) DEFAULT NULL,
  `birthday` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

INSERT INTO `users` (`id`, `name`, `name_kana`, `email`, `gender`, `age`, `blood_type`, `birthday`)
VALUES
	(1,'大村郁恵','おおむらいくえ','oomuraikue@example.com','F',39,'B','1978/1/23'),
	(2,'片岡寿明','かたおかとしあき','kataokatoshiaki@example.com','M',21,'A','1995/4/21'),
	(3,'岡島慎之介','おかじましんのすけ','okajimashinnosuke@example.com','M',20,'O','1997/2/4'),
	(4,'中田英嗣','なかたひでつぐ','nakatahidetsugu@example.com','M',23,'AB','1994/1/3'),
	(5,'藤村涼子','ふじむらりょうこ','fujimuraryouko@example.com','F',33,'O','1984/1/28'),
	(6,'相川一輝','あいかわかずき','aikawakazuki@example.com','M',35,'A','1981/8/18'),
	(7,'野崎由美子','のざきゆみこ','nozakiyumiko@example.com','F',12,'O','2005/1/20'),
	(8,'井口愛子','いぐちあいこ','iguchiaiko@example.com','F',18,'B','1998/8/6'),
	(9,'小森愛','こもりあい','komoriai@example.com','F',30,'O','1986/7/17'),
	(10,'那須由美子','なすゆみこ','nasuyumiko@example.com','F',13,'A','2003/10/5');
```

でははじめていきます。

## WHERE句で条件指定をして集計をする。
集合関数はなにも条件がなければ対象の全レコードに対して集計を行いますが、
WHERE句で条件に一致したレコードに対して集計することももちろんできます。

```
mysql> SELECT COUNT(*) AS "人数" FROM users WHERE gender = "F";
+--------+
| 人数   |
+--------+
|      6 |
+--------+
1 row in set (0.00 sec)
```

ASはエイリアスといわれるものです。
AS "人数"というものをつけないと集計結果で項目がCOUNT(*)という意味のわかりにくい名前になってしまいます。
意味がわかりにくかったり、文字数が長かったりしてSQLとして読みにくい場合には適宜エイリアスをつけることをおすすめします。

## GROUP別で集計をする。
上記のように一つ一つのグループの条件をWHEREで指定して集計することももちろんできますが、
GROUP BYを使用すると一回で全てのグループに対して同様の集計を行うことができます。

```
mysql> SELECT gender,COUNT(*) AS "人数" FROM users GROUP BY gender;
+--------+--------+
| gender | 人数   |
+--------+--------+
| F      |      6 |
| M      |      4 |
+--------+--------+
2 rows in set (0.00 sec)
```

COUNT以外の全ての集合関数は本来集合ごとに適用するためのものなので同様のことが行えます。


## GROUP BYで複数のカラムを指定する
GROUP BYで複数のカラムを指定するとより詳細な集計結果を出力することができます。
これは説明するより見たほうが早いです。
例：性別・血液型別の人数を集計する。

```
mysql> SELECT gender,blood_type,COUNT(*) AS "人数" FROM users GROUP BY gender,blood_type;
+--------+------------+--------+
| gender | blood_type | 人数   |
+--------+------------+--------+
| F      | A          |      1 |
| F      | B          |      2 |
| F      | O          |      3 |
| M      | A          |      2 |
| M      | AB         |      1 |
| M      | O          |      1 |
+--------+------------+--------+
6 rows in set (0.00 sec)
```


## HAVINGで集計結果をフィルターする
GROUP BYでグループごとに分けたはいいものの、
全てを利用したくない場合もあると思います。
そのときにはHAVING句を用いて集計結果をフィルターします。

HAVING句もWHERE句のように条件を柔軟に記述することができます。
例：性別・血液型別の人数を集計し、人数が2人以上のグループの結果だけ取得する。

```
mysql> SELECT gender,blood_type,COUNT(*) AS "人数" FROM users GROUP BY gender,blood_type HAVING 人数 >= 2;
+--------+------------+--------+
| gender | blood_type | 人数   |
+--------+------------+--------+
| F      | B          |      2 |
| F      | O          |      3 |
| M      | A          |      2 |
+--------+------------+--------+
3 rows in set (0.00 sec)
```