【Oracle】ORA-00937の原因と解決方法|not a single-group group function・GROUP BYなしで集計関数と通常列を混在した時の直し方

【Oracle】ORA-00937の原因と解決方法|not a single-group group function・GROUP BYなしで集計関数と通常列を混在した時の直し方 Oracle

ORA-00937: not a single-group group function は、Oracleで SUMCOUNTAVGMAXMIN などの集計関数と、通常の列を GROUP BY なしで同じ SELECT 句に書いたときに発生するエラーです。

たとえば「部署ID」と「全体の売上合計」を同時に出そうとして、SELECT department_id, SUM(amount) FROM sales のように書くと、Oracleは department_id をどの単位で1行にまとめればよいか判断できません。

先に結論
ORA-00937は、通常列を表示したいなら GROUP BY を追加し、全体集計だけが欲しいなら通常列をSELECTから外すと直ります。明細行を残したまま集計値も出したい場合は、SUM(...) OVER (...) のような分析関数を使います。
スポンサーリンク

ORA-00937とは

Oracle公式の説明では、ORA-00937は、AVGCOUNTMAXMINSUMSTDDEVVARIANCE などのグループ関数と、個別の列式を同じSELECTリストに含めたのに、その個別列が GROUP BY 句に含まれていない場合に発生します。

ポイントは「集計結果は1行にまとまるのに、通常列は複数行ぶん存在する」ことです。SQLとして、全体で1行にしたいのか、部署ごと・顧客ごと・月ごとに複数行へ分けたいのかを明示する必要があります。

やりたいこと 正しい考え方 使う構文
全体の合計だけ出す 通常列を表示しない SELECT SUM(amount)
部署ごとの合計を出す 部署IDでグループ化する GROUP BY department_id
明細行に部署合計も付ける 行をまとめず分析関数を使う SUM(...) OVER (PARTITION BY ...)
集計結果とマスタ名を出す 先に集計してからJOINする サブクエリ / WITH

もっとも多い原因:集計関数と通常列を混ぜている

典型例は、通常列と集計関数を同時にSELECTしているのに、GROUP BYを書いていないパターンです。

mixed-aggregate-column-ng.sql
-- NG: department_id は通常列、SUM(amount) は集計関数
SELECT department_id,
       SUM(amount) AS total_amount
FROM sales;

部署ごとの合計が欲しいなら、部署IDを GROUP BY に入れます。

add-group-by-ok.sql
-- OK: department_id ごとに集計する
SELECT department_id,
       SUM(amount) AS total_amount
FROM sales
GROUP BY department_id;

GROUP BYの基本は SQLのGROUP BYで件数をカウントする方法、合計の考え方は SUM関数の使い方 が参考になります。

全体集計だけが欲しいなら通常列を外す

部署別ではなく、テーブル全体の合計や件数だけが欲しい場合、通常列をSELECTから外します。GROUP BYを足すと結果が部署ごとに分かれてしまうため、欲しい結果が変わります。

single-group-aggregate-ok.sql
-- OK: 全体で1行の集計結果だけを返す
SELECT COUNT(*) AS sales_count,
       SUM(amount) AS total_amount,
       AVG(amount) AS avg_amount
FROM sales
WHERE order_date >= DATE '2026-01-01';

全体集計では、GROUP BY を書かなくてもテーブル全体が1つのグループとして扱われます。そのため、SELECTできるのは定数、集計関数、集計式などに限られます。件数は COUNT関数、平均は AVG関数 も確認してください。

GROUP BYを足すと集計粒度が変わる

ORA-00937を直すために GROUP BY を追加するのはよくある修正ですが、集計単位が変わる点に注意します。全体で1行だった集計が、部署ごと、顧客ごと、日付ごとに複数行へ分かれます。

grouping-level-difference.sql
-- 全体で1行
SELECT SUM(amount) AS total_amount
FROM sales;

-- 部署ごとに複数行
SELECT department_id,
       SUM(amount) AS total_amount
FROM sales
GROUP BY department_id;

SQLが通るかどうかだけでなく、「何ごとの合計なのか」を先に決めます。集計粒度を変えたくないなら、通常列を外すか、サブクエリや分析関数に分けます。

COUNTと通常列を混ぜたケース

COUNT は特にORA-00937を起こしやすい集計関数です。一覧画面で「部署名と件数」を出したいときに、GROUP BYを書き忘れるパターンがよくあります。

count-with-column.sql
-- NG: department_name と COUNT(*) を混ぜているが GROUP BY がない
SELECT department_name,
       COUNT(*) AS employee_count
FROM employees;

-- OK: 部署名ごとに件数を数える
SELECT department_name,
       COUNT(*) AS employee_count
FROM employees
GROUP BY department_name;

MAX/MINと通常列を混ぜたケース

MAXMIN でも同じです。「最終注文日と顧客名」を出したい場合、単に MAX(order_date)customer_name を並べるだけでは、どの顧客名を表示すべきか決まりません。

max-with-column.sql
-- NG: customer_name が集計単位として決まらない
SELECT customer_name,
       MAX(order_date) AS latest_order_date
FROM orders;

-- OK: 顧客ごとの最終注文日
SELECT customer_name,
       MAX(order_date) AS latest_order_date
FROM orders
GROUP BY customer_name;

「全体で最も新しい注文の顧客名」を取りたいなら、GROUP BYではなくサブクエリや分析関数で該当行を取り出します。この違いを混同すると、SQLは通っても欲しい結果と違う集計になります。

get-row-for-max-value.sql
-- 全体で最も新しい注文の行を取りたい場合
SELECT customer_name,
       order_date
FROM orders
WHERE order_date = (
    SELECT MAX(order_date)
    FROM orders
);

-- 同日の行が複数ある場合は複数行返る

1行だけに絞りたい場合は、ROW_NUMBER() を使って並び順を明示します。MAX値と一緒に他の列を出したいケースでは、単純なGROUP BYよりこの形のほうが意図を保ちやすいです。

row-number-for-max-row.sql
SELECT customer_name,
       order_date,
       amount
FROM (
    SELECT customer_name,
           order_date,
           amount,
           ROW_NUMBER() OVER (ORDER BY order_date DESC, order_id DESC) AS rn
    FROM orders
)
WHERE rn = 1;

明細行も集計値も出したいなら分析関数を使う

明細行を残したまま、同じ行に合計や件数を付けたい場合は、通常の集計関数ではなく分析関数を使います。分析関数は行をまとめず、各行に集計値を付加できます。

analytic-function-fix.sql
-- 明細行を残しながら部署ごとの合計を表示する
SELECT employee_id,
       department_id,
       amount,
       SUM(amount) OVER (PARTITION BY department_id) AS department_total
FROM sales;

分析関数の詳しい使い方は Oracleの分析関数の使い方 にまとめています。GROUP BYは行をまとめる、分析関数は行を残す、と分けて考えると選びやすくなります。

全行に全体件数や部署内件数を付けたい場合も、COUNT(*) OVER が使えます。一覧画面で「明細 + 総件数」や「明細 + 部署内件数」を表示したいときに便利です。

count-over-for-detail-rows.sql
SELECT employee_id,
       department_id,
       employee_name,
       COUNT(*) OVER () AS all_employee_count,
       COUNT(*) OVER (PARTITION BY department_id) AS department_employee_count
FROM employees;

サブクエリに分けると直しやすいケース

集計結果とマスタ列を一緒に表示したい場合は、先に集計してからJOINすると読みやすくなります。集計対象の列と表示用の列を分けられるため、ORA-00937を避けやすくなります。

aggregate-then-join.sql
WITH sales_summary AS (
    SELECT department_id,
           SUM(amount) AS total_amount
    FROM sales
    GROUP BY department_id
)
SELECT d.department_id,
       d.department_name,
       s.total_amount
FROM departments d
JOIN sales_summary s
  ON s.department_id = d.department_id;

CASE式と集計関数で発生するケース

条件付き集計で CASE と集計関数を混ぜる場合も注意します。行単位の列をSELECTに残したまま集計関数を書くとORA-00937になります。

case-aggregate.sql
-- OK: CASE式をSUMの中に入れて、全体で1行の条件付き集計にする
SELECT SUM(CASE WHEN status = 'PAID' THEN amount ELSE 0 END) AS paid_amount,
       SUM(CASE WHEN status = 'UNPAID' THEN amount ELSE 0 END) AS unpaid_amount
FROM invoices;

-- OK: 顧客ごとに条件付き集計するならGROUP BYを追加する
SELECT customer_id,
       SUM(CASE WHEN status = 'PAID' THEN amount ELSE 0 END) AS paid_amount
FROM invoices
GROUP BY customer_id;

CASE式の条件集計は OracleのCASE式 でも詳しく扱っています。

DISTINCTではORA-00937の根本解決にならない

ORA-00937が出たとき、DISTINCT を付ければ重複が消えて直るのでは、と考えることがあります。しかし、DISTINCT は結果行の重複を除くための機能で、集計関数と通常列を同時にSELECTするルールを変えるものではありません。

distinct-does-not-fix-ora00937.sql
-- NG: DISTINCT を付けても、通常列と集計関数の混在は解決しない
SELECT DISTINCT department_id,
       SUM(amount) AS total_amount
FROM sales;

-- OK: department_id ごとの集計ならGROUP BYを書く
SELECT department_id,
       SUM(amount) AS total_amount
FROM sales
GROUP BY department_id;

重複を消したいのか、集計したいのかを分けて考えます。重複排除が目的ならDISTINCT、集計が目的ならGROUP BYや集計関数を使います。

ORA-00979との違い

ORA-00937とORA-00979はどちらも集計とGROUP BYに関係するため混同しやすいです。ざっくり言うと、ORA-00937は「GROUP BYがないのに集計関数と通常列を混ぜた」、ORA-00979は「GROUP BYはあるが、GROUP BYに含まれない列や式が残っている」エラーです。

エラー 典型パターン 直し方
ORA-00937 SELECT dept, SUM(amount) FROM sales GROUP BY dept を足す、または dept を外す
ORA-00979 SELECT dept, name, SUM(amount) FROM sales GROUP BY dept name をGROUP BYへ入れる、または集計/削除する

GROUP BY句がないならまずORA-00937、GROUP BY句があるのに列が足りないなら ORA-00979の原因と解決方法 を確認してください。

直し方の判断フロー

質問 YESの場合 NOの場合
全体で1行の集計が欲しい? 通常列をSELECTから外す 次へ
列ごと・部署ごと・月ごとに集計したい? GROUP BY を追加する 次へ
明細行を残したい? 分析関数を使う サブクエリで集計してJOINする
マスタ名など表示用の列が必要? 先に集計してからJOINする 集計SQLをシンプルにする

修正チェックリスト

手順 確認すること 判断
1 SELECT 句に集計関数があるか SUMCOUNTAVG など
2 同じSELECT句に通常列があるか department_idcustomer_name など
3 GROUP BY があるか なければORA-00937の典型
4 欲しい結果が全体1行か複数行か 全体1行なら通常列を外す
5 集計単位が決まっているか 部署ごとなら GROUP BY department_id
6 明細行を残したいか 残すなら分析関数を検討する
7 SQLが複雑すぎないか サブクエリやWITH句に分ける

よくある質問

GROUP BYを付ければ必ず直りますか?

SQLエラーは直ることがありますが、集計粒度が変わるため、必ず正しい結果になるとは限りません。全体集計が欲しいなら通常列を外し、部署別や顧客別にしたいならGROUP BYを追加します。

COUNT(*)と通常列を一緒に出したい場合はどうしますか?

通常列ごとの件数が欲しいなら、その通常列をGROUP BYに入れます。明細行に全体件数を付けたいなら COUNT(*) OVER () のような分析関数を使います。

MAX値の行の他の列も表示したい場合は?

MAX と通常列をただ並べるのではなく、サブクエリでMAX値を求めて該当行をJOINするか、ROW_NUMBER() などの分析関数で1行を選びます。

DISTINCTを付ければ直りますか?

基本的には直し方として適切ではありません。DISTINCT は重複行を除く機能で、集計関数と通常列の混在ルールを解決するものではありません。

ORA-00937とORA-00979のどちらを見ればよいですか?

GROUP BY句がないならORA-00937、GROUP BY句があるのにSELECTやORDER BYに未グループ化列が残っているならORA-00979を疑います。

まとめ

ORA-00937は、GROUP BYなしで集計関数と通常列を同じSELECT句に書いたときに発生します。まず、欲しい結果が全体で1行なのか、部署ごと・顧客ごとなど複数行なのかを決めます。

全体集計なら通常列を外し、グループ別集計ならGROUP BYを追加します。明細行を残したい場合は、通常の集計ではなく分析関数やサブクエリを使うと安全です。ORA-00979との違いも押さえると、集計SQLのエラーをかなり速く切り分けられます。

参考

ORA-00937 – Oracle Database Error Help

SELECT – Oracle SQL Language Reference