MySQLにおけるHAVING句の実践的な使い方とWHEREとの違い

MySQLのHAVING句は、GROUP BYによる集計後に各グループに対して条件を適用するための構文です。対してWHERE句は、集計の前段階で個々の行をフィルタリングします。この根本的な実行タイミングの違いが、両者の用途と制約を決定づけます。

基本的な動作の違い

  • WHERE:テーブルから読み込まれた直後の行に対して評価。集計関数(例:COUNT(), SUM(), AVG())を含めることはできません。
  • HAVINGGROUP BYによるグループ化と集計処理が完了した後、各グループ単位で評価。集計結果を条件に使用できます。

実用例で理解する

例1:地域ごとの人口合計と面積合計を取得

SELECT region, SUM(population) AS total_pop, SUM(area) AS total_area
FROM world_countries
GROUP BY region;

このクエリは、region列でレコードをグループ化し、それぞれのグループについて人口と面積の合計を計算します。

例2:面積合計が100万平方キロメートルを超える地域のみを抽出

SELECT region, SUM(population) AS total_pop, SUM(area) AS total_area
FROM world_countries
GROUP BY region
HAVING total_area > 1000000;

WHERE area > 1000000では意図通りの結果が得られません。なぜなら、元のテーブルには「地域全体の面積」というカラムは存在せず、それはSUM(area)という集計によって初めて生成される値だからです。

重複データの検出

メールアドレスの重複を特定するケース:

SELECT email, COUNT(*) AS occurrence_count
FROM user_profiles
GROUP BY email
HAVING occurrence_count > 1;

このクエリは、まず全ユーザーをemailでグループ化し、次に各グループの件数をカウント。最後に、そのカウント値が2以上であるグループのみを返します。

文字列長のチェック(補足)

集計とは無関係ですが、よく併用される文字列操作の例として:

SELECT url FROM websites
WHERE CHAR_LENGTH(TRIM(url)) BETWEEN 2 AND 9;

これはWHEREで十分なケースであり、HAVINGは不要です。

ORDER BYとの位置関係

HAVINGGROUP BYの直後、かつORDER BYの前に記述しなければなりません。以下の順序は必須です:

  1. GROUP BY
  2. HAVING
  3. ORDER BY

誤った順序(例:GROUP BY ... ORDER BY ... HAVING)は構文エラー(エラー1064)を引き起こします。

性能上の考慮

可能な限りフィルタリングはWHEREで行うべきです。例えば、特定の都市名を持つレコードだけを集計対象にしたい場合:

-- 効率的:集計前に不要な行を除外
SELECT city, AVG(temp_lo) 
FROM weather 
WHERE city IN ('Tokyo', 'Osaka') 
GROUP BY city;

-- 非効率的:全レコードを集計後に絞り込む
SELECT city, AVG(temp_lo) 
FROM weather 
GROUP BY city 
HAVING city IN ('Tokyo', 'Osaka');

HAVINGは集計済みの結果セットに対して作用するため、WHEREよりも遅い可能性があります。不要な集計処理を減らすためにも、事前の行フィルタリングを優先しましょう。

タグ: MySQL SQL group-by having where

6月29日 19:30 投稿