【新人教育 資料】第8章 SQLへの道 〜グループ編〜

#【新人教育 資料】第8章 SQLへの道 〜グループ編〜
## あらすじ
新人がいっぱい入ってくる。新人のレベルもバラバラ。教育資料も古くなっているので、更新しましょう。
どうせなら、公開しちゃえばいいじゃん。という流れになり、新人教育用の資料を順次更新していくことにしました。

※後々、リクエストに応じて更新することが多いのでストックしておくことをおすすめします。

自分は[TEMONA株式会社](http://temona.co.jp/)でCTOをしていますが、頭でっかちに理論ばっかり学習するよりは、イメージがなんとなく掴めるように学習し、実践の中で知識を深めていく方が効率的に学習出来ると考えています。

※他の登壇やインタビュー記事は[Wantedly](https://www.wantedly.com/users/105935)から見てください。


教育スタイルとしては正しい事をきっちりかっちり教えるのではなく、未経験レベルの人がなんとなく掴めるように、資料を構成していきます。

以下のようなシリーズネタで進めます。

|No.|記事|
|:-----------|-----------|
|1|[【新人教育 資料】第1章 SQLへの道 〜DB編〜](http://qiita.com/devopsCoordinator/items/9b70e506150888e190be)|
|2|[【新人教育 資料】第2章 SQLへの道 〜3値論理編〜](http://qiita.com/devopsCoordinator/items/9c10410b50f8fcc2ba79)|
|3|[【新人教育 資料】第3章 SQLへの道 〜基本数学編〜](http://qiita.com/devopsCoordinator/items/08e405a9c58cc203d016)|
|4|[【新人教育 資料】第4章 SQLへの道 〜SQL基本操作編〜](http://qiita.com/devopsCoordinator/items/0f742158caf0fdb533e3)|
|5|[【新人教育 資料】第5章 SQLへの道 〜絞込編〜](http://qiita.com/devopsCoordinator/items/dca5aa9c155292f1c6ea)|
|6|[【新人教育 資料】第6章 SQLへの道 〜ソート編〜](http://qiita.com/devopsCoordinator/items/8cad032aa035dc1cdbc0)|
|7|[【新人教育 資料】第7章 SQLへの道 〜集合関数（SUM、MAX、MIN、AVG、COUNT）編〜](http://qiita.com/devopsCoordinator/items/85918c8508dddf19586f)|
|8|[【新人教育 資料】第8章 SQLへの道 〜グループ編〜](http://qiita.com/devopsCoordinator/items/f74228615982932c914b)|
|9|[【新人教育 資料】第9章 SQLへの道 〜結合編〜](http://qiita.com/devopsCoordinator/items/3ba154429973a4ddc9e0)|

では、今回もはじめていきましょう！

# グループ
こちらは前回の
[【新人教育 資料】第7章 SQLへの道 〜集合関数（SUM、MAX、MIN、AVG、COUNT）編〜](http://qiita.com/devopsCoordinator/items/85918c8508dddf19586f)
の発展編です。
まだこちらを読んでいない方はそちらの一読をお願いします。

前回はある条件の総数や総和、最大、最小そして平均を出す方法を見てきました。
たとえば、全ての注文の「県ごと」の人数を知りたい際はどうすれば良いでしょうか。

もちろん、下記のように頑張ればいけます。

```sql
select count(*) from orders where prefectural = '北海道';
select count(*) from orders where prefectural = '青森県';
select count(*) from orders where prefectural = '大分県';
```

![10.jpg](https://qiita-image-store.s3.amazonaws.com/0/103225/b7e024ea-df1d-192f-12d5-364d87ebe812.jpeg)

エンジニアは面倒臭がりです。
これを全県分作れば結果が得られますが、実はこんなことしなくても一発で出る方法があります。

![11.jpg](https://qiita-image-store.s3.amazonaws.com/0/103225/f5e78edd-696b-7e1b-eb47-f7066ecd481b.jpeg)

```sql
SELECT count(*),prefectural FROM orders GROUP BY prefectural;
```
それが今回紹介する
group by
です。

# GROUP BY
group by
は
「〜ごと」
という処理を行います。

上記の例では、全ての注文の「県ごと」の人数を出しています。
この「県ごと」という部分が

```sql
GROUP BY prefectural;
```
の部分です。

基本構造は下記の形です。

```sql
SELECT 関数名(カラム名１),カラム名２ FROM テーブル名 GROUP BY カラム名２;
```
カラム名１が計算されるカラム
カラム名２が「〜ごと」の部分のカラムです。

select部分のカラム名２はなくても動きますが、無いと困ります。
どう困るかわからない方はやってみてください。

いくつか紹介して行きましょう。
### 例

・全ての顧客の年齢ごとの人数を調べる
![12.jpg](https://qiita-image-store.s3.amazonaws.com/0/103225/f33a00c2-8f3e-5786-1eac-261cb7c0d6a7.jpeg)


```sql
select count(*) as 人数,age as 年齢 from users where age is not null group by age order by age;
```
年齢「ごと」の人数を出しています。


・出身地ごとに、性別ごとの最高齢と最年少を調べています。
![13.jpg](https://qiita-image-store.s3.amazonaws.com/0/103225/8dd6cf5f-d541-a40a-5e32-05a3a3e5bbe5.jpeg)


```sql
SELECT max(age) AS 最高齢,
       min(age) AS 最年少,
       birthplace AS 出身地,
       (CASE
            WHEN gender_id = 0 THEN '男性'
            ELSE '女性'
        END) AS 性別
FROM users
WHERE age IS NOT NULL
GROUP BY birthplace,
         gender_id
ORDER BY birthplace,
         gender_id;
```

ちなみに

```sql
(CASE
     WHEN gender_id = 0 THEN '男性'
     ELSE '女性'
END) AS 性別
```
の部分はcase文と呼ばれ、
基本構文は

```sql
case when カラム名 = 値１ then AAA else BBB end
```
のような条件文です。

```sql
when カラム名 = 値１ then AAA
```
の部分は幾つでも増やすことができます。
読んで字のごとくなのですが、この場合 gender_id が 0 の場合は「男性」それ以外の場合は「女性」を返します。

group by を使えば非常に便利なことがわかっていただけるかと思います。
それをさらに便利にするのが having です。
## HAVING

```sql
select count(*) as 人数,age as 年齢 from users group by age order by age;
```
上記に出てきたこちらのSQLを用います。
たとえば、この中で人数が25人以上の年齢を出したい場合にhavingが使えます。
![14.jpg](https://qiita-image-store.s3.amazonaws.com/0/103225/c6eb62bc-cdf4-4bac-2ac6-4c0040dd3226.jpeg)

```sql

SELECT count(*) AS 人数,
       age AS 年齢
FROM users
WHERE age IS NOT NULL
GROUP BY age
HAVING count(*) >= 25
ORDER BY age ;
```
having は集合関数の結果をもとに絞り込むことができます。
基本構造は下記の形です。

```sql
SELECT 関数名(カラム名１),カラム名２ FROM テーブル名 GROUP BY カラム名２ HAVING 関数名(カラム名１);
```


### 例
下記3つの違い、わかりますか?
①

```sql
SELECT avg(age) AS 平均年齢,
       count(*) AS 人数,
       birthplace AS 出身地
FROM users
WHERE age IS NOT NULL
  AND age >= 40
GROUP BY birthplace ;
```
②

```sql
SELECT avg(age) AS 平均年齢,
       count(*) AS 人数,
       birthplace AS 出身地
FROM users
WHERE age IS NOT NULL
GROUP BY birthplace
HAVING avg(age) >= 40;
```
③

```sql
SELECT avg(age) AS 平均年齢,
       count(*) AS 人数,
       birthplace AS 出身地
FROM users
WHERE age IS NOT NULL
  AND age >= 40
GROUP BY birthplace
HAVING avg(age) >= 50;
```
①は出身地ごとの40歳以上の方の平均年齢と人数と出身地
![15.jpg](https://qiita-image-store.s3.amazonaws.com/0/103225/2c2b3073-8fd7-223d-5883-fa3e8dd09b81.jpeg)

②は出身地ごとの平均年齢が40歳以上の平均年齢と人数と出身地
![16.jpg](https://qiita-image-store.s3.amazonaws.com/0/103225/25a527fb-dd03-f6cf-eea7-32e4430eb584.jpeg)

③は出身地ごとの40歳以上の方の平均年齢が50歳以上の平均年齢と人数と出身地です。
![17.jpg](https://qiita-image-store.s3.amazonaws.com/0/103225/8a21c1aa-beed-8234-b955-b94be50a5562.jpeg)


こう書くと、日本語の方がややこしいですね。

## 「ちょっと寄り道〜基本数学編〜」
[【新人教育 資料】第3章 SQLへの道 〜基本数学編〜](http://qiita.com/devopsCoordinator/items/08e405a9c58cc203d016)
で出てきた基本数学を利用すれば各データをSQL上でこねくり回せます。色々遊んでみてください。
例

```sql
SELECT count(*) AS 人数,
       max(age) AS 県内最高年齢,
       min(age) AS 県内最低年齢,
       avg(age) AS 県平均年齢,
       (max(age) - min(age)) AS 県内最大年齢差,
       (max(age) + min(age)) AS 県内最大年齢と最小年齢の和,
       (avg(age) * 2) AS 平均２倍,
       (max(age) + min(age)) - (avg(age) * 2) AS 県内最大年齢と最小年齢の和と平均の２倍の差,
       birthplace AS 出身地
FROM users
WHERE age IS NOT NULL
GROUP BY birthplace
ORDER BY 県内最大年齢と最小年齢の和と平均の２倍の差;
```

#　演習
・血液型ごとにそれぞれ何人いるか情報を取得
・県ごとに最高齢と最年少の年齢差の情報を取得し、その差順に並べる

# 参考文献
mysql5.6 リファレンスマニュアル:https://dev.mysql.com/doc/refman/5.6/ja/

# あとがき
これで集合関数は完了です。
ここまで使いこなせればSQLが非常に便利なツールであるということが実感できると思います。
次回の【新人教育 資料】第9章 SQLへの道 〜結合編〜
まで使えるようになれば一人前のSQL初心者と言えるのでは無いでしょうか。