> ## Documentation Index
> Fetch the complete documentation index at: https://villagesql.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# MySQL の HAVING

> MySQL の HAVING 句が GROUP BY で作られたグループをどのように絞り込むか、WHERE との違い、そして WHERE には使えない列のエイリアスを HAVING では使える理由。

> [card] [VillageSQL is a drop-in replacement for MySQL with extensions.](https://villagesql.com/docs/en/8.4/stable/install-server.md)
>
> All examples on this page work on VillageSQL. Install Now →

`WHERE` はグループ化される前に行を除外します。`HAVING` はグループが作られた後に
グループ全体を除外します。この 1 文がこのレッスンのすべてであり、以下はすべて
その帰結です。

始める前に `mysql -u root -p sakila` でクライアントを開いてください。

## HAVING の仕組み

```sql theme={null}
SELECT GROUPING_COLUMN, AGGREGATE_FUNCTION(COLUMN_NAME)
FROM TABLE_NAME
GROUP BY GROUPING_COLUMN
HAVING GROUP_CONDITION;
```

`GROUPING_COLUMN` をグループを定義する列に、`TABLE_NAME` をテーブルに、
`AGGREGATE_FUNCTION(COLUMN_NAME)` をグループごとに求めたいものに、
`GROUP_CONDITION` を `COUNT(*) > 200` のようなグループ全体に適用する条件に
置き換えます。

サーバーはまずすべてのグループを作り、次に条件が真であるグループだけを
残します。

## 例

### 大きなグループだけを残す

```sql theme={null}
SELECT rating, COUNT(*) AS films
FROM film
GROUP BY rating
HAVING COUNT(*) > 200
ORDER BY rating;
```

```text theme={null}
+--------+-------+
| rating | films |
+--------+-------+
| PG-13  |   223 |
| NC-17  |   210 |
+--------+-------+
2 rows in set (0.01 sec)
```

5 つのレーティングすべてが数えられました。そのうち 3 つのグループが条件を
満たさず、除外されました。

### HAVING ではエイリアスを使える

エイリアスは結果の列に名前を付けるものであり、`HAVING` が実行される時点では
結果が存在しています。

```sql theme={null}
SELECT rating, COUNT(*) AS films
FROM film
GROUP BY rating
HAVING films > 200
ORDER BY rating;
```

```text theme={null}
+--------+-------+
| rating | films |
+--------+-------+
| PG-13  |   223 |
| NC-17  |   210 |
+--------+-------+
2 rows in set (0.00 sec)
```

同じ 2 行が返り、書く量は少なくなります。`WHERE` にはこれができません。
名前を付ける対象となる結果が存在する前に実行されるためです。
[WHERE](https://villagesql.com/docs/ja/tutorial/where.md) のレッスンでは、代わりに返される `ERROR 1054` を
示しています。

### 両方の句を 1 つのクエリで使う

実際のクエリの多くは両方を使います。`WHERE` で行を選び、`HAVING` でグループを
選びます。

```sql theme={null}
SELECT rating, COUNT(*) AS films
FROM film
WHERE length > 120
GROUP BY rating
HAVING COUNT(*) > 100
ORDER BY rating;
```

```text theme={null}
+--------+-------+
| rating | films |
+--------+-------+
| PG-13  |   118 |
+--------+-------+
1 row in set (0.00 sec)
```

`WHERE` が 120 分を超える映画を残し、グループ化がレーティングごとにそれを
数え、`HAVING` が 100 本を超える 1 つのレーティングを残しました。

## 集計関数は WHERE には書けない

先ほどの `HAVING` 条件を `WHERE` に移すと、サーバーはそれを即座に拒否します。

```sql theme={null}
SELECT rating, COUNT(*) AS films
FROM film
WHERE COUNT(*) > 200
GROUP BY rating;
```

```text theme={null}
ERROR 1111 (HY000): Invalid use of group function
```

`WHERE` はグループ化の前に実行されるため、テストすべき件数がまだありません。
集計関数のエイリアスにも同じことが当てはまりますが、エラーは異なります。

```sql theme={null}
SELECT rating, COUNT(*) AS films
FROM film
WHERE films > 200
GROUP BY rating;
```

```text theme={null}
ERROR 1054 (42S22): Unknown column 'films' in 'where clause'
```

どちらも意味は同じで、直し方も同じです。条件を `HAVING` に移します。

`WHERE` のエイリアスで出る `ERROR 1054` には 2 つの対処法があり、集計関数か
どうかで区別します。単純な式のエイリアスは `WHERE` で式を繰り返して書けます。
これは [WHERE](https://villagesql.com/docs/ja/tutorial/where.md) のレッスンで示しています。集計関数の
エイリアスは、上記の `ERROR 1111` のため、そこで繰り返すことがまったく
できないので、`HAVING` に移す必要があります。

## どの句が何をテストするか

| 句 | 実行時点 | テスト対象 | 例 |
| - | - | - | - |
| `WHERE` | グループ化の前 | 1 行 | `WHERE length > 120` |
| `HAVING` | グループ化の後 | 1 つのグループ全体 | `HAVING COUNT(*) > 50` |

行をテストする条件は常に `WHERE` に置き、`HAVING` はグループが存在して
初めて意味を持つものに限ってください。それがコストの低い構成でもあります。
その理由は
[MySQL の GROUP BY のパフォーマンス](https://villagesql.com/docs/ja/guides/group-by-having.md)ガイドで
計測しています。

> [!NOTE]
>
> `HAVING` で選択リストのエイリアスを指定するのは MySQL の拡張です。同じ SQL を
> 別のデータベースでも実行する必要がある場合は、エイリアスではなく集計関数を
> 繰り返して書いてください。

## まとめ

* グループ全体を除外するには `HAVING` を、行を除外するには `WHERE` を使います。
* `HAVING` は `GROUP BY` の後に実行されるため、集計関数をテストできます。
* `ERROR 1111` は、`HAVING` に書くべき集計関数が `WHERE` にあるという意味です。
* `HAVING` では列のエイリアスを指定できます。`WHERE` にはできません。
* グループではなく行をテストする条件は `WHERE` に置きます。

## 関連項目

* [GROUP BY](https://villagesql.com/docs/ja/tutorial/group-by.md) — この句が絞り込むグループを作る
* [WHERE](https://villagesql.com/docs/ja/tutorial/where.md) — 先に実行される句
* [集計関数](https://villagesql.com/docs/ja/tutorial/aggregate-functions.md) — グループ条件が通常テストするもの
* [MySQL の GROUP BY のパフォーマンス](https://villagesql.com/docs/ja/guides/group-by-having.md) — 絞り込む前にグループ化するコスト


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.