SQL GROUP BY と ORDER BY の順序・使い分け
実務で頻出する集計クエリは、句の順序と選択列の制約を押さえれば迷いません。本稿では 書き順・評価順、SELECT に書ける列の制約、 PostgreSQL / MySQL / BigQuery の挙動差をまとめます。
A concise reference for the write-order, evaluation-order, SELECT-column constraints, and dialect differences between GROUP BY and ORDER BY.
TL;DR
- 書き順:
SELECT ... FROM ... WHERE ... GROUP BY ... HAVING ... ORDER BY ... LIMIT - 評価順:
FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT SELECTに書ける非集計列はGROUP BYに含まれる列のみ(MySQL以外)ORDER BYは SELECT エイリアスや 1,2... の列番号で指定可
1. 典型クエリ / Canonical query
SELECT user_id,
COUNT(*) AS orders,
SUM(amount) AS total
FROM orders
WHERE created_at >= '2026-01-01'
GROUP BY user_id
HAVING SUM(amount) >= 10000
ORDER BY total DESC
LIMIT 50;WHERE は行フィルタ、HAVING は集計後フィルタ。混同すると パフォーマンス劣化や意図しない結果になります。
2. SELECT に書ける列の制約 / Column rule
| DB / Dialect | GROUP BY 外の列 | 挙動 |
|---|---|---|
| PostgreSQL | 不可 | column must appear in GROUP BY |
| MySQL (ONLY_FULL_GROUP_BY on) | 不可 | 5.7+ デフォルトで有効 |
| MySQL (off) | 可 | 任意行が返る(非決定的) |
| BigQuery | 不可 | ANY_VALUE() で明示 |
| SQL Server / Oracle | 不可 | ANSI 準拠 |
3. ORDER BY の書き方 / ORDER BY tips
- SELECT で付けたエイリアス(例:
ORDER BY total DESC)は PostgreSQL/MySQL/BigQuery で動作 - 列番号 (
ORDER BY 2 DESC) は可読性が落ちるため本番クエリでは避ける - NULL 順序は DB 依存。明示したい場合は
ORDER BY col NULLS LAST
4. GROUP BY の落とし穴 / Pitfalls
- MySQL の
ONLY_FULL_GROUP_BYを無効化したコードを他DBに移植すると崩れる DISTINCTとGROUP BYはほぼ同等だが、集計列を足す時はGROUP BY一択- 巨大テーブルでは
ORDER BYの前にLIMIT付きサブクエリで削ると劇的に速い
5. ROLLUP / CUBE で小計・総合計を追加する / Subtotals with ROLLUP and CUBE
グループごとの集計に加えて小計・総合計の行も同時に欲しい場合、GROUP BY ROLLUP(...)が便利です。 毎回UNION ALLで集計行を積み上げる必要がありません。
SELECT region, category, SUM(amount) AS total
FROM sales
GROUP BY ROLLUP(region, category)
ORDER BY region, category;
-- region, category ごとの小計行と、全体の総合計行(両方NULL)が追加される| DB / Dialect | ROLLUP | CUBE |
|---|---|---|
| PostgreSQL | 対応 | 対応 |
| MySQL | 対応(WITH ROLLUP構文) | 非対応 |
| SQL Server / Oracle | 対応 | 対応 |
| BigQuery | 対応 | 対応 |
MySQLはCUBE非対応な点に注意してください。全次元の組み合わせ集計が必要ならPostgreSQLやBigQueryへの移行、または個別クエリのUNION ALLで代替します。
6. GROUP BYとウィンドウ関数(OVER)の使い分け / GROUP BY vs window functions
GROUP BYは行を1グループ1行に集約しますが、個別の行を保持したまま集計値も 一緒に表示したい場合はウィンドウ関数(OVER句)を使います。
-- GROUP BY: user_idごとに1行に集約される
SELECT user_id, SUM(amount) AS total FROM orders GROUP BY user_id;
-- ウィンドウ関数: 元の行数を保ったまま各行にグループ合計を付与
SELECT order_id, user_id, amount,
SUM(amount) OVER (PARTITION BY user_id) AS user_total
FROM orders;「注文明細は残したいが、ユーザーごとの合計も各行に表示したい」といったケースはウィンドウ関数が適しています。
7. パフォーマンス:インデックスとORDER BY / Indexing for GROUP BY + ORDER BY
大きなテーブルでGROUP BYとORDER BYを併用すると、ソートのために一時領域へ 書き出す処理(filesortなど)が発生しやすくなります。GROUP BYする列とORDER BYする列を まとめてカバーする複合インデックスを用意しておくと、オプティマイザがソート済みの順序をそのまま 利用できる場合があります。
-- (user_id, created_at) の複合インデックスがあると、
-- 以下のGROUP BY + ORDER BYがソート処理を省略できることがある
CREATE INDEX idx_orders_user_created ON orders (user_id, created_at);
SELECT user_id, COUNT(*) FROM orders GROUP BY user_id ORDER BY user_id;実際にインデックスが使われているかはEXPLAIN(PostgreSQL/MySQL)や実行計画で確認してください。オプティマイザの挙動はDB・バージョンにより異なります。
8. English summary
In SQL, GROUP BY is always written before ORDER BY. The full evaluation order is FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT, which also explains why HAVING can reference aggregates but WHEREcannot. In all major dialects except MySQL with ONLY_FULL_GROUP_BY disabled, every non-aggregate column in SELECT must appear in GROUP BY. When porting MySQL code, validate against Postgres or BigQuery early.
よくある質問 / FAQ
- GROUP BY と ORDER BY はどちらを先に書く?
- SQL の構文上は GROUP BY → HAVING → ORDER BY の順。論理的な評価順も同じです。
- Which comes first, GROUP BY or ORDER BY?
- GROUP BY is written (and evaluated) before ORDER BY. The full clause order is FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT.
- SELECT に GROUP BY 外の列を書ける?
- PostgreSQL/Oracle/SQL Server は不可(エラー)。MySQL は ONLY_FULL_GROUP_BY を外すと許容するが非推奨。集計関数または GROUP BY 列のみに留めるのが安全です。