2025年10月31日金曜日

SQL Server GROUP BYとHAVING句の使い方|集計関数の基本と実務例

SQL Server GROUP BYとHAVING句の使い方|集計関数の基本と実務例

SQL Serverでデータを分析する際に欠かせないのがGROUP BYHAVING句です。
これらは「データをグループ化して集計する」ための構文で、売上集計や件数カウントなど、業務システムのレポート機能で頻繁に登場します。
本記事では、GROUP BYとHAVINGの基本構文に加えて、SUM・AVG・COUNTなどの代表的な集計関数、そして実務でそのまま使えるサンプルクエリと課題形式の例まで丁寧に解説します。

目次

GROUP BYとは?基本構文と使い方

GROUP BYの基本構文

GROUP BY句は、同じ値を持つ行を1つのグループにまとめて、グループ単位で集計を行うための構文です。


SELECT 列名, 集計関数(列名)
FROM テーブル名
GROUP BY 列名;

例えば「顧客ごとの売上金額」を集計したい場合は、次のように書きます。


SELECT CustomerId,
       SUM(SalesAmount) AS TotalSales
FROM dbo.Sales
GROUP BY CustomerId;

このSQLでは、同じ CustomerId ごとに売上が合計され、顧客単位の集計結果が1行ずつ返されます。

GROUP BYのポイント

  • GROUP BYで指定した列の組み合わせごとに1グループになる(複数列も指定可能)。
  • SELECT句に書けるのは「GROUP BYで指定した列」か「集計関数」だけ(その他の列を書くとエラー)。
  • 集計関数を使わない列を表示したい場合は、必ずGROUP BYにその列を追加する。
  • NULLは1つのグループとして扱われる(NULL同士は同じグループ)。

-- 複数列でグループ化する例(店舗×月単位の売上)
SELECT StoreId,
       FORMAT(OrderDate, 'yyyy-MM') AS SalesMonth,
       SUM(SalesAmount) AS MonthlySales
FROM dbo.Sales
GROUP BY StoreId,
         FORMAT(OrderDate, 'yyyy-MM');

代表的な集計関数の使い方

主な集計関数一覧

SQL Serverでよく使う集計関数は次のとおりです。

関数名 説明 使用例
SUM() 合計値を求める SUM(SalesAmount)
AVG() 平均値を求める AVG(SalesAmount)
COUNT() 件数を数える COUNT(*), COUNT(CustomerId)
MAX() 最大値を求める MAX(UnitPrice)
MIN() 最小値を求める MIN(UnitPrice)

複数の集計を同時に出す例


SELECT Category,
       COUNT(*)        AS 商品数,
       AVG(UnitPrice)  AS 平均価格,
       MAX(UnitPrice)  AS 最高価格,
       MIN(UnitPrice)  AS 最低価格
FROM dbo.Products
GROUP BY Category;

1回のGROUP BYで複数の集計関数を同時に使うことで、1つの結果セットで多角的な集計情報を確認できます。

HAVING句の役割とWHERE句との違い

HAVING句の基本構文

HAVING句は、GROUP BYで集計した「グループ単位の結果」に対して条件を指定するための句です。


SELECT CustomerId,
       SUM(SalesAmount) AS TotalSales
FROM dbo.Sales
GROUP BY CustomerId
HAVING SUM(SalesAmount) >= 100000;

このSQLでは、「売上合計が10万円以上の顧客だけ」を抽出します。
WHEREでは「行」に対する条件しか指定できないのに対して、HAVINGでは集計値(SUM・AVGなど)を条件に使える点が大きな違いです。

WHERE句との違いを整理しよう

適用タイミング 対象 主な用途
WHERE GROUP BYより前 個々の行 元データの絞り込み(行フィルタ)
HAVING GROUP BYで集計した後 グループ(集計結果) 集計値に対する絞り込み

簡単に言えば、WHEREは「集計前の行を絞る」・HAVINGは「集計後のグループを絞る」構文です。


-- NG例:本来WHEREで書くべき条件をHAVINGに書いているパターン
SELECT CustomerId,
       SUM(SalesAmount) AS TotalSales
FROM dbo.Sales
GROUP BY CustomerId
HAVING OrderDate >= '2025-01-01';

-- 正しい例:日付条件はWHERE、売上合計の条件はHAVING
SELECT CustomerId,
       SUM(SalesAmount) AS TotalSales
FROM dbo.Sales
WHERE OrderDate >= '2025-01-01'
GROUP BY CustomerId
HAVING SUM(SalesAmount) >= 100000;

パフォーマンスの観点でも、行レベルの絞り込みはWHEREに書くのが基本です。

実務でよく使う集計クエリ例

例1:月別売上の合計を求める


SELECT FORMAT(OrderDate, 'yyyy-MM') AS 月,
       SUM(SalesAmount) AS 月間売上
FROM dbo.Sales
GROUP BY FORMAT(OrderDate, 'yyyy-MM')
ORDER BY 月;

FORMAT関数を使うと、日付を「年-月」単位に整形してグループ化できます。
シンプルな月次レポートでよく使うパターンです。

例2:社員ごとの平均売上を求め、条件付きで抽出


SELECT EmployeeId,
       AVG(SalesAmount) AS 平均売上
FROM dbo.Sales
GROUP BY EmployeeId
HAVING AVG(SalesAmount) > 50000;

「平均売上が5万円を超える社員だけ」を取り出す典型的なHAVING句の使い方です。

例3:商品カテゴリ別の売上ランキング


SELECT Category,
       SUM(SalesAmount) AS TotalSales
FROM dbo.Sales
GROUP BY Category
ORDER BY TotalSales DESC;

カテゴリ単位で売上を集計し、合計値の大きい順に並べます。
ECサイトの売れ筋分析などでよく利用されるパターンです。

例4:特定期間の売上件数と合計金額(WHERE+GROUP BY)


SELECT FORMAT(OrderDate, 'yyyy-MM-dd') AS 日付,
       COUNT(*)        AS 注文件数,
       SUM(SalesAmount) AS 売上合計
FROM dbo.Sales
WHERE OrderDate >= '2025-01-01'
  AND OrderDate <  '2025-02-01'
GROUP BY FORMAT(OrderDate, 'yyyy-MM-dd')
ORDER BY 日付;

WHEREで期間を絞り込んだ上で、日別に件数と金額を集計しています。
期間レポート・日別ダッシュボードなどにそのまま使える形です。

例5:一定件数以上売れている商品だけを抽出


SELECT ProductId,
       COUNT(*) AS 注文件数
FROM dbo.Sales
GROUP BY ProductId
HAVING COUNT(*) >= 10;

「一定以上売れている商品」や「一定以上利用している顧客」など、しきい値でグループをフィルタしたい場合にHAVINGが活躍します。

実務課題と回答例

課題1:顧客ごとの売上合計と件数を集計せよ

要件:顧客ごとに「売上合計」と「注文件数」を集計し、売上合計の高い順に並べなさい。


SELECT CustomerId,
       COUNT(*)        AS OrderCount,
       SUM(SalesAmount) AS TotalSales
FROM dbo.Sales
GROUP BY CustomerId
ORDER BY TotalSales DESC;

課題2:月次売上が100万円以上の月だけを抽出せよ

要件:月別に売上合計を計算し、合計が100万円以上の月のみ抽出しなさい。


SELECT FORMAT(OrderDate, 'yyyy-MM') AS SalesMonth,
       SUM(SalesAmount) AS MonthlySales
FROM dbo.Sales
GROUP BY FORMAT(OrderDate, 'yyyy-MM')
HAVING SUM(SalesAmount) >= 1000000
ORDER BY SalesMonth;

課題3:社員ごとの平均単価が指定値を超えるケースを抽出せよ

要件:社員ごとに「売上金額の平均」を求め、平均が8,000円を超える社員だけを抽出しなさい。


SELECT EmployeeId,
       AVG(SalesAmount) AS AvgSalesAmount
FROM dbo.Sales
GROUP BY EmployeeId
HAVING AVG(SalesAmount) > 8000;

課題4:カテゴリごとの商品数と平均価格を集計せよ

要件:カテゴリごとに商品数と平均価格を集計し、平均価格の高い順に並べなさい。


SELECT Category,
       COUNT(*)      AS ProductCount,
       AVG(UnitPrice) AS AvgPrice
FROM dbo.Products
GROUP BY Category
ORDER BY AvgPrice DESC;

まとめ

学んだ内容の整理

  • GROUP BYでデータをグループ化し、集計関数(SUM・AVG・COUNT など)でグループ単位の集計値を出せる。
  • SELECT句には、GROUP BYで指定した列か集計関数だけを書くのがルール。
  • HAVING句は「集計後の結果」に対する条件指定であり、行レベルの条件はWHEREに書くのが基本。
  • WHEREは集計前、HAVINGは集計後に適用されるため、処理順序を意識することでパフォーマンスも改善できる。
  • 実務では「月次集計」「社員別集計」「カテゴリ別ランキング」などの形で頻出する。

GROUP BYとHAVINGは、SQL Serverでデータ分析を行ううえでの基本かつ強力な武器です。この記事のサンプルや課題を試しながら、自分のシステムのテーブル構成に置き換えて練習してみてください。

参考リンク

SQL Server 解説用イメージ

※2025/11/25 更新

0 件のコメント:

コメントを投稿