🧚 お知らせ!Beekeeper Studio は高速でモダンなオープンソースのデータベース GUI です ダウンロード
2026年1月8日 著者: Matthew Rathbone

GROUP BY は同じ値を持つ行をグループにまとめます。これにより、COUNT、SUM、AVG などの集計関数はテーブル全体で1つではなく、グループごとに1つの結果を返します。例は PostgreSQL、MySQL、SQLite、SQL Server で動作します。

GROUP BY の使い方

GROUP BY の後にグループ化する列を指定し、SELECT する他の列はすべて集計関数(COUNT、SUM、AVG、MIN、MAX)で囲みます。異なる値ごとに1行ずつ返されます:

SELECT group_column, AGGREGATE(value_column)
FROM table_name
GROUP BY group_column;
-- Input: scores
-- player | team | points
-- -------+------+-------
-- Ana    | Red  | 12
-- Ben    | Blue | 9
-- Cleo   | Red  | 7
-- Dev    | Blue | 15
-- Eli    | Red  | 4

SELECT team, SUM(points) AS total_points, COUNT(*) AS players
FROM scores
GROUP BY team;

-- Output:
-- team | total_points | players
-- -----+--------------+--------
-- Red  | 23           | 3
-- Blue | 24           | 2

5行がチームごとに1行ずつ、計2行になり、各集計はそのチームの行だけを対象にします。

知っておくべきルール

  • SELECT する列はすべて GROUP BY に含めるか、集計関数の中に入れる必要があります。そうしないとグループに表示する値が1つに決まらず、クエリはエラーになります:
-- Input: products
-- name    | category
-- --------+---------
-- Blender | Kitchen
-- Toaster | Kitchen
-- Pillow  | Bedroom

SELECT name, category, COUNT(*) AS product_count
FROM products
GROUP BY category;

-- ERROR: column "products.name" must appear in the GROUP BY clause
--   or be used in an aggregate function
-- (SQLite runs it anyway and takes name from one row in each group)

列を GROUP BY に追加するか、たとえば MIN(name) のようにその列も集計することで修正できます。

  • NULL は1つのグループになります。グループ化する列が NULL の行はすべて同じグループに入ります。COALESCE を使ってラベルを付けましょう:
-- Input: products
-- name        | category
-- ------------+-----------
-- Notebook    | Stationery
-- Gift Card   | NULL
-- Pen Set     | Stationery
-- Mystery Box | NULL
-- Stapler     | Stationery

SELECT category, COUNT(*) AS product_count
FROM products
GROUP BY category;

-- Output:
-- category   | product_count
-- -----------+--------------
-- NULL       | 2
-- Stationery | 3

SELECT COALESCE(category, 'Uncategorized') AS category, COUNT(*) AS product_count
FROM products
GROUP BY category;

-- Output:
-- category      | product_count
-- --------------+--------------
-- Uncategorized | 2
-- Stationery    | 3
  • WHERE はグループ化の前に行を絞り込み、HAVING はグループ化の後にグループを絞り込みます。COUNT(*) > 2 のような集計を条件にできるのは HAVING だけです。集計が不要な条件は WHERE に書けば、グループ化する行が少なくなります。
  • グループの並び順は決まっていません。たとえば上記のスコアのクエリでは、SQLite と SQL Server は Red より先に Blue を返します。順序が重要な場合は必ず ORDER BY を追加してください。

別の書き方

式を繰り返す代わりに、列の別名や SELECT リスト内の列の位置でグループ化することもできます:

-- Input: orders
-- order_id | amount
-- ---------+-------
-- 1        | 35.00
-- 2        | 240.00
-- 3        | 18.50
-- 4        | 120.00
-- 5        | 64.00

SELECT CASE WHEN amount >= 100 THEN 'large' ELSE 'small' END AS order_size,
       COUNT(*) AS order_count
FROM orders
GROUP BY order_size  -- or GROUP BY 1
ORDER BY order_size;
-- SQL Server allows neither: repeat the CASE expression in GROUP BY

-- Output:
-- order_size | order_count
-- -----------+------------
-- large      | 2
-- small      | 3

ROLLUP は小計と総計を追加します。小計の行では、合計対象の列が NULL で表示されます:

-- Input: sales
-- region | category | amount
-- -------+----------+-------
-- East   | Books    | 100.00
-- East   | Toys     | 50.00
-- West   | Books    | 80.00
-- East   | Books    | 40.00

SELECT region, category, SUM(amount) AS total_sales
FROM sales
GROUP BY ROLLUP(region, category);
-- MySQL: GROUP BY region, category WITH ROLLUP
-- SQLite has no ROLLUP

-- Output:
-- region | category | total_sales
-- -------+----------+------------
-- East   | Books    | 140.00
-- East   | Toys     | 50.00
-- East   | NULL     | 190.00
-- West   | Books    | 80.00
-- West   | NULL     | 80.00
-- NULL   | NULL     | 270.00

集計関数を使わない GROUP BY は、異なる値をそれぞれ1回ずつ返します。これは SELECT DISTINCT category FROM products と同じです。

GROUP BY はいつ使うべき?

「〜ごと」や「〜それぞれ」を問う場面では GROUP BY を使います。顧客ごとの注文数、月ごとの売上、部署ごとの従業員数などです。ほとんどの用途は次の4つのいずれかに当てはまります。

グループごとに行数を数える

ステータスごとの注文数、カテゴリごとの商品数、日ごとの登録者数などです。COUNT(*) はグループ内のすべての行を数え、COUNT(column) はその列が NULL でない行だけを数えます。

グループごとの合計、平均、範囲

SUM、AVG、MIN、MAX は各グループの値を1つの数値にまとめます。顧客ごとの売上、平均注文額、カテゴリ内で最も安い商品と最も高い商品などです。1つのクエリで必要なだけ返せます。

複数の列で結果を分類する

GROUP BY の後に複数の列を並べると、組み合わせごとに1行が返されます。地域別・月別の売上、倉庫別・商品タイプ別の在庫などです。データに存在する組み合わせだけが表示されます。

必要なグループだけを残す

HAVING はグループが作られた後、その集計値を使ってグループを絞り込みます。10人を超える部署、$1,000 以上使った顧客、または2回以上出現する値などです。最後の例は重複を見つける方法そのものです。

GROUP BY の実例

空のカテゴリも含めて、全カテゴリの商品数を数える

GROUP BY は存在する値に対してのみグループを作ります。商品のないカテゴリも一覧に含めるには、categories テーブルから始めて products を LEFT JOIN します。* ではなく結合したテーブルの列を数えてください。空のカテゴリにも NULL で埋められた1行が残り、COUNT(*) はそれを数えてしまいます。

-- Input: categories
-- category_name
-- -------------
-- Audio
-- Cameras
-- Drones

-- Input: products
-- id | name       | category
-- ---+------------+---------
-- 1  | Earbuds    | Audio
-- 2  | Soundbar   | Audio
-- 3  | Action Cam | Cameras

-- Wrong: COUNT(*) counts the NULL row of an empty category
SELECT c.category_name, COUNT(*) AS product_count
FROM categories c
LEFT JOIN products p ON p.category = c.category_name
GROUP BY c.category_name
ORDER BY c.category_name;

-- Output:
-- category_name | product_count
-- --------------+--------------
-- Audio         | 2
-- Cameras       | 1
-- Drones        | 1

-- Right: COUNT(p.id) skips it
SELECT c.category_name, COUNT(p.id) AS product_count
FROM categories c
LEFT JOIN products p ON p.category = c.category_name
GROUP BY c.category_name
ORDER BY c.category_name;

-- Output:
-- category_name | product_count
-- --------------+--------------
-- Audio         | 2
-- Cameras       | 1
-- Drones        | 0

売上上位3カテゴリを求める

カテゴリごとの売上を合計し、合計で並べ替えて上位3件を残します。Books は個々の売上が Toys の1件の売上より小さいにもかかわらず、2位になります:

-- Input: purchases
-- category | amount
-- ---------+-------
-- Books    | 45.00
-- Toys     | 120.00
-- Garden   | 310.00
-- Books    | 80.00
-- Music    | 25.00

SELECT category, SUM(amount) AS revenue
FROM purchases
GROUP BY category
ORDER BY revenue DESC
LIMIT 3;
-- SQL Server has no LIMIT: write SELECT TOP 3 category, ... instead

-- Output:
-- category | revenue
-- ---------+--------
-- Garden   | 310.00
-- Books    | 125.00
-- Toys     | 120.00

地域別・月別の売上をレポートする

地域と、日付から取り出した月でグループ化すると、各地域に月ごとに1行ずつ返されます:

-- Input: sales
-- region | sale_date  | amount
-- -------+------------+-------
-- East   | 2024-01-08 | 120.00
-- West   | 2024-01-15 | 80.00
-- East   | 2024-01-22 | 95.00
-- East   | 2024-02-03 | 60.00
-- West   | 2024-02-11 | 150.00

SELECT region, EXTRACT(MONTH FROM sale_date) AS month, SUM(amount) AS total_sales
FROM sales
GROUP BY region, EXTRACT(MONTH FROM sale_date)
ORDER BY region, month;

-- Output:
-- region | month | total_sales
-- -------+-------+------------
-- East   | 1     | 215.00
-- East   | 2     | 60.00
-- West   | 1     | 80.00
-- West   | 2     | 150.00

SQL Server と SQLite には EXTRACT がありません。SQL Server では MONTH(sale_date)、SQLite では strftime('%m', sale_date) を使ってください。データが複数年にまたがる場合は、年でもグループ化しましょう。

今年のリピート顧客を見つける

WHERE で2024年の注文だけを残し、HAVING でそのうち2件以上ある顧客を残します。顧客 202 は合計2件の注文がありますが、2024年は1件だけなので除外されます:

-- Input: orders
-- order_id | customer_id | order_date
-- ---------+-------------+-----------
-- 1        | 201         | 2023-11-20
-- 2        | 202         | 2023-12-05
-- 3        | 201         | 2024-01-09
-- 4        | 202         | 2024-02-14
-- 5        | 201         | 2024-03-02

SELECT customer_id, COUNT(*) AS orders_2024
FROM orders
WHERE order_date >= '2024-01-01'
GROUP BY customer_id
HAVING COUNT(*) >= 2;

-- Output:
-- customer_id | orders_2024
-- ------------+------------
-- 201         | 2

GROUP BY についてさらに学ぶ

このブログの関連記事:

公式ドキュメント:

Beekeeper Studioは無料のオープンソースのデータベースGUIです

これまで使った中で最高のSQLクエリ&エディタツールです。データベースの管理に必要なものがすべて揃っています。 - ⭐⭐⭐⭐⭐ Mit

Beekeeper Studioは高速で直感的、そして使いやすいツールです。多数のデータベースに対応し、Windows、Mac、Linuxで快適に動作します。

BeekeeperのLinux版は100%フル機能。手抜きも機能の妥協も一切ありません。

Beekeeper Studio 利用者の声

★★★★★
「Beekeeper Studio は以前の SQL ワークフローを完全に置き換えました。高速で直感的で、データベース作業がまた楽しくなりました。」
— Alex K.、データベース開発者
★★★★★
「多くのデータベース GUI を試してきましたが、Beekeeper は機能とシンプルさのバランスが完璧です。とにかくちゃんと動きます。」
— Sarah M.、フルスタックエンジニア