SQL学習入門 | WHERE/ORDER BY/GROUP BY/HAVING　基本構文を学習してみた

※学習中のため間違っている箇所があるかもしれません。
# はじめに
前回はSQLの基本構文である`SELECT`/`INSERT`/`UPDATE`/`DELETE`を学習しました。
今回は上記基本構文に追加して、集約、整形するためのクエリ`WHERE`/`ORDER BY`/`GROUP BY`/`HAVING`を学習していきます。

# ダミーデータの作成
まず、上記SQL文を使用するためにダミーデータを作成していきましょう。
ダミーデータとは、テストやデモ用の開発環境で使用される架空のデータです。
A5M2の機能で簡単に作成できるので、クエリ練習前にダミーデータを作成していきましょう。

### 対象のテーブルをダブルクリック
今回は以前作成したusersテーブルにデータを挿入していきます。
そのため「mysqlstudy」→「テーブル」→「users」の順で選択していきましょう。
![a5m2mysql.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2155918/2e47cf50-356b-4713-bfe0-121d3f808044.png)

### ダミーデータ作成画面
まず、上部にある丸いボタンをクリックしましょう。
※画像の赤枠内
![a5m2mysql.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2155918/10240609-85fa-4c3c-91ad-3009a9c819b6.png)

そうすると、テスト用ダミーデータ作成画面が表示されます。
※既存データをすべて削除する警告が出ますが、今回は気にせず削除しましょう。
実際の現場で使用する場合は、ほかへの影響がないかどうかを確認してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2155918/d2fa4ce1-5a38-4948-9a0d-a15367acb198.png)

### 値の設定
テスト用ダミーデータ生成画面では、カラムとフォーマットが表示されています。
これはカラムに対して、どのような値を生成するかを設定する項目です。
開始ボタンを押してデータを作成しましょう。

※以下の値に関してフォーマットを変更しています。
皆さんは作成しているテーブルに合わせて変更しましょう。
name：FULLNAME_jp
email：EMAIL
role：'USER'
delete_flg：'0'
created_at：DATE
updated_at：DATE

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2155918/e51c054b-f36c-47d0-b550-7050eef12257.png)

※補足：A5M2以外で使用することはないですが、以下にフォーマットを簡単にまとめます。
%d：1,2,3...といった連番を付与します。
%.010d：0000000001,000000002...といった先頭10文字を0埋めした連番を付与します。
NOW:これは現在時刻を挿入します。

※「ステップ」に関して
ステップは数値を一回にどれだけ増やすかを表しています。
1に設定した場合は、0,1,2,3～999となり、2に設定すると0,2,4～となります。

#### 以下のエラーが出る場合
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2155918/763328db-0262-437a-adbe-d3d47a85243b.png)
このエラーはテーブルのAUTO_INCREMENTが影響している可能性があります。
AUTO_INCREMENTはテーブル作成時にオプションで指定し、特定のカラムを自動連番で挿入する機能ですが、
前のデータを削除する際にAUTO_INCREMENTがリセットされていない可能性があります。
その際は以下の構文を使用しましょう。
```sql
TRUNCATE TABLE users;
```
これはテーブルの中身をすべて削除し、リセットする構文です。
データがすべて消えてしまうので業務で使用する際は注意して使用しましょう。

### データの作成
期待通り、ダミーデータが作成されていれば完成です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2155918/ae3819bd-62b4-4d78-b7e5-63f772021f68.png)

# WHERE
`WHERE`句は`SELECT`、`DELETE`、`UPDATE`の基本構文を使用する際に、検索結果や更新対象に条件を指定するための句です。
※INSERTに関しては、レコードを挿入するだけなので`WHERE`区を使用することはありません。

## 構文
### SELECT
```
SELECT * FROM テーブル名 WHERE カラム名=条件
```

### UPDATE
```
UPDATE テーブル名 SET カラム名=値 WHERE カラム名=条件
```

### DELETE
```
DELETE FROM テーブル名 WHERE カラム名=条件
```

それぞれ「カラム=条件」で指定した値に合致しているものを対象とします。
先ほど作成したダミーデータで確認していきましょう。

#### WHERE区確認
それでは一つずつ結果を確認していきましょう。
##### SELECT
以下で名前が'七上隆太'のレコードのみに絞って検索することができます。
```sql
SELECT * FROM users WHERE name='七上隆太'
```
##### 結果
nameカラムが`七上隆太`のレコードだけ取得できているのがわかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2155918/f71035fa-447c-4b51-a8f7-f6cf917021b1.png)

##### UPDATE
UPDATEでは検索条件を名前が`七上隆太`のカラムに絞って、そのカラムの名前を`七上テスト`に変更します。
```sql
UPDATE users SET name='七上テスト' WHERE name='七上隆太'
```
##### 結果
1件のレコードが正常に更新されているのがわかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2155918/74c998ae-c365-4a3f-86f4-4a63bd6f5364.png)

検索してみると、id=1の名前が`七上テスト`に変更されているのがわかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2155918/9c1c887e-4890-41eb-b93a-36cd194d8b57.png)

##### DELETE
DELETEでは検索条件を名前が`七上テスト`のカラムに絞って、そのカラム1件を削除します。
```sql
DELETE FROM users WHERE name='七上テスト'
```
##### 結果
1件のレコードが正常に削除されているのがわかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2155918/e72d96b6-79ac-4a54-b4fa-32b55943eb1b.png)

検索してみると、データが出力されません。
正常に削除されているようです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2155918/ab9d780d-5efb-4c4f-a25f-449e48150d08.png)

このようにWHERE区では条件を決めて、検索、更新、削除を行うことができます。
実務でも使用することが多いのでしっかりと学んでいきましょう。

## 複数WHERE区を使用する
WHERE区を使用する場合、カラムを複数条件として指定することができます。
今回は`SELECT`で動作を確認していきましょう。

構文
```
構文1
SELECT * FROM users WHERE カラム=条件 AND カラム < 条件
構文2
SELECT * FROM users WHERE カラム=条件 OR カラム < 条件
```

### AND
ANDを使用することで
`A AND B`のAとBの両方に合致する値を取得することができます。

以下のようなデータがあったとします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2155918/229a05a5-2970-4d56-a158-600b58883a7e.png)

name='神沼正介'が複数存在していて、名前だけでは特定のレコードを取得できません。
試しに名前だけを条件として取得してみましょう。

このようにデータが3件取得できます。
これでは特定のレコードを一件取得したい場合に、不要なカラムまで取得してしまいます。
そこで検索条件を追加し、さらに絞っていきましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2155918/80ff2a2d-b74e-4bb8-a38e-964cddd2fb42.png)

以下の構文を使用することで
`name='神沼正介'` と `id > 10`の両方に合致する値を取得できます。
```sql
SELECT * FROM users WHERE name='神沼正介' AND id > 10;
```

#### 実行結果
複数の条件を設定することで、特定の一件を取得することに成功しました。
このように複数条件を指定するのは業務上でも特に使用するので覚えておきましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2155918/79922f9b-918e-406e-96d1-ef39b66d8620.png)

### OR
ORを使用することで
`A OR B`のA または Bに合致する値を取得することができます。

以下のデータで名前が`久山光助`とidが5以下の値両方の値を取得したい状況があったとします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2155918/64921799-17b6-4b3d-be65-5395e3ed0944.png)

以下の構文を使用することで
`name='神沼正介'` と `id <= 5`に合致する値をそれぞれ取得することができます。
```sql
SELECT * FROM users WHERE name='神沼正介' OR id <= 5;
```

#### 実行結果
このようにidが5以下のレコードと名前が`神沼正介`のレコード両方を取得することができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2155918/c5c43dd3-d830-4bb0-8e1b-aa2c304c7e51.png)

`AND`や`OR`は業務でもよく使用するためしっかり確認しておきましょう。

# ORDER BY
次は`ORDER BY`に関して学習していきます。
ORDER BYは検索結果の並び替えに使用します。

何を基準に並び替えるのか、降順、昇順を指定することで、結果を好きな順番に並び替えることができます。

## 構文
```
SELECT * FROM テーブル名 ORDER BY カラム名 DESC(またはASC)
```
DESC：降順(最大値が先頭)
ASC：昇順(最小値が先頭)

実際にダミーデータで試してみましょう。

```sql
SELECT * FROM users ORDER BY name desc;
```
#### 実行結果
このように`name desc`と指定することで、通常はid順で表示されますが、
名前を基準とし、降順に並び替えることができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2155918/ff221577-3c80-4317-b119-77a949914695.png)

これも業務ではよく使用されるものです。
身近なシステムでいえば、ECサイトなどで使用する商品の並び替えでもこのORDER BYを使用しています。

# GROUP BY
GROUP BYは指定した値をグループ化し、集約関数を使用し、平均値、件数、最大値、最小値を出力することができます。
グループ化とは、例えばusersテーブルの場合`name`を指定することで複数同じ名前が`name`カラムに存在する場合、それらをひとつのレコードにまとめることができます。

## テーブル作成
今回のGROUP BYではusersテーブルだとあまり適切ではないため、新たにテーブルを作成しましょう。
今回は簡単な商品テーブルを作成します。
```sql
CREATE TABLE items (
    id int AUTO_INCREMENT PRIMARY KEY NOT NULL,
    name VARCHAR(50) NOT NULL,
    price int NOT NULL,
    purchase_date timestamp default current_timestamp,
    user_id int NOT NULL,
    FOREIGN KEY (user_id) REFERENCES users(id)
)
```

### テーブルのダミーデータを作成しましょう
(同じ商品名が欲しいので、nameは手動で入力)
※id=1は削除したので2からスタート
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2155918/387a2a28-1bce-4f95-94a4-92760e4201da.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2155918/ff60b88a-8f48-435d-9f1b-ffa86b68b03a.png)
これでテーブルのデータが作成されました
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2155918/d8b1a7f1-241c-4bff-af75-4cd618c959ab.png)

それでは作成したデータでGROUP BYを学んでいきましょう。
### 構文
```
SELECT カラム名 FROM テーブル名 GROUP BY カラム名, 条件
```
※`GROUP BY`を使用したい場合、`*`(全選択)を使用することはできません。

まずは`GROUP BY`を使用してみましょう。
```sql
SELECT name FROM items GROUP BY name;
```

#### 結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2155918/cc051b22-8e26-4594-ac28-0ca74574f14f.png)

このようにnameカラムが2つだけ出力されました。
`GROUP BY`で`name`を指定したことで、重複しているnameがすべて一行にまとめられているということです。

これを利用することで、商品の販売合計価格や販売数を簡単に取得することができます。

## 合計
```sql
SELECT name, SUM(price) FROM items GROUP BY name;
```
※SUMは対象のカラムの合計値を取得することあできる関数です。

### 結果
これで二つの商品の販売合計価格が取得できました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2155918/fef991fd-6baa-4dfe-883c-62a12862d5c4.png)

## 平均
```sql
SELECT name, AVG(price) FROM items GROUP BY name;
```
※AVG：平均値を取得します。

### 結果
priceの平均値を取得することができました

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2155918/ef6be33f-416d-44ab-a665-cbd0fa2548e6.png)

## 個数
```sql
SELECT name, COUNT(price) FROM items GROUP BY name;
```
※COUNT：件数を取得します。

### 結果
各項目の件数を取得できました

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2155918/a891c68a-17b6-454e-873a-f870b59e0185.png)

## 注意
以下のように、priceカラムを使用する際に、関数を使用せずに記載するとエラーが発生するので気を付けてください。
```sql
SELECT name, price FROM items GROUP BY name;
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2155918/e95aa44b-5cab-4d6a-b4a7-32c3e3761abd.png)


※補足
また、これ以外にも最大値、最小値を取得するための関数があり、
それもGROUP BYと合わせて使用することができます。

#### 最大値
```sql
SELECT name, MAX(price) FROM items GROUP BY name;
```
#### 最小値
```sql
SELECT name, MIN(price) FROM items GROUP BY name;
```


# HAVING
HAVINGは出力する値をフィルターしたい場合に使用します。
```
SELECT カラム名 FROM テーブル名 GROUP BY カラム名 HAVING 条件式
```

条件式には主に
カラム=値
NOT NULL
などが入ります

先ほどの商品テーブルで試していきましょう。

まず、わかりやすいようにデータを少し追加
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2155918/1306c4e3-5ce1-41e1-ad81-dbdba730b39b.png)

`HAVING`は基本的に`GROUP BY`と一緒に使用します。
※理由は後述

以下を実際に実行してみましょう。
※`HAVING`は通常`GROUP BY`とセットで使用し、集計結果に条件をかける際に使用します。

まずは`GROUP BY`で全体を一度集計してい見ましょう。
```sql
SELECT name, SUM(price) FROM items GROUP BY name;
```
デカビタ、サイダー、コーラに集計されました。
`SUM`を使用しているため、各レコードの合計値が出力されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2155918/0b5f5ee7-5ac2-411d-8c28-4b0048ee227f.png)

`HAVING`はこれをさらに絞り込むために使用します。

例えば売り上げが200000円以上の商品を知りたい場合、以下SQLで取得することができます。
```sql
SELECT name, SUM(price) AS price FROM items GROUP BY name HAVING price > 200000;
```

### 結果
結果として合計金額が200000円以上の商品が出力されました。
つまり集計(グループ化)→フィルターといった流れです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2155918/5f4e7e53-8b5e-472b-b392-70b14b6c9e5a.png)

#### 疑問
HAVINGとWHEREに関して、何が違うのか？
と思う方がいると思います。(私です)

HAVINGとWHEREは基本的に動作は同じです。
何が違うのかというと、実行順序が違います。
SQLは下記順序で基本的に実行されていきます

FROM>JOIN>WHERE>GROUP BY>HAVING>SELECT>DISTINCT>ORDER BY>LIMIT

つまり、`WHERE`は`GROUP BY`でグループ化される前に条件を適用します。
`HAVING`は`GROUP BY`でグループ化された後に条件を適用します。

そのため`GROUP BY`の使用や集計関数の有無でどちらを使うか判断していきましょう。


# まとめ
今回は以下の構文を学習しました。
- `WHERE`:条件指定
- `ORDER BY`:並び替え
- `GROUP BY`:グループ化
- `HAVING`:条件指定

これらの語句は業務でもたびたび使用することがあるので、しっかり覚えておきましょう。

次回は`INNER JOIN`/`LEFT JOIN`/`集計関数`について学習していきます。
引き続き、SQLの基礎力を高める学習記事を投稿していきます！

[前：SQL学習入門 | INSERT/SELECT/DELETE/UPDATE　基本構文を学習してみた](https://qiita.com/suica1010/items/f28774e16e7f589029ef)
[次：SQL学習入門 ｜ INNER JOINとOUTER JOINの違いを実例で解説](https://qiita.com/suica1010/items/ce76be2580faec9689d4)
参考文献
[【22日間で学ぶ】SQL文、分析関数、テーブル設計、SQLチューニングまでMySQLで覚えるSQL実践講座](https://www.udemy.com/course/3sqlmysql/)