データ分析

QUERY GROUP BYの「cannot group without agg」修正方法

ModelMonkey2026年6月13日読了約 2 分

なぜエラーが発生するのか

Google SheetsのQUERY関数は、Google Visualization APIクエリ言語上で動作しており、SQLに近い厳密なルールに従っています。公式ドキュメントにはこう明記されています。「GROUP BYを使用する場合、SELECT句内のすべての列は、GROUP BY句に列挙されるか、集計関数を使用していなければならない。」

例外も曖昧さも一切ありません。すべての列は「ディメンション(GROUP BYに列挙)」か「メジャー(集計関数でラップ)」のどちらかです。この2つの役割が混在した瞬間、QUERYは cannot group without agg で処理を止めます。

具体的な例で確認しましょう。「売上」シートに、日付(A列)・地域(B列)・SKU(C列)・売上(D列)・売上原価(E列)というデータがあるとします。地域別の売上を集計しようとして、次のように書いたとします。

=QUERY('売上'!A:E, "SELECT B, C, SUM(D) GROUP BY B", 1)

このクエリはエラーになります。C列(SKU)がSELECT句にあるにもかかわらず、GROUP BYにも集計関数にも含まれていないためです。修正方法は2つあります。

修正方法① SKUをGROUP BYに追加する(地域×SKUの組み合わせで1行ずつ出力):

=QUERY('売上'!A:E, "SELECT B, C, SUM(D) GROUP BY B, C", 1)

修正方法② SKUをSELECTから削除する(地域単位で1行ずつ出力):

=QUERY('売上'!A:E, "SELECT B, SUM(D) GROUP BY B", 1)

どちらが正解かは、必要なアウトプット次第です。地域単位の売上合計だけでよいならC列を削除し、地域×SKUの内訳が必要ならC列を残してGROUP BYに追加する。クエリの構文の問題ではなく、何を集計したいかという設計の問題です。

列を体系的に確認する「列監査」の習慣

6列以上を含む取締役会向けの資料(ボードパック)用クエリを書く場合、実行前にSELECT句の全列を確認する習慣をつけましょう。以下は第1四半期(Q1)の実績を正しくグループ化して取得する例です。

=QUERY(
  '売上'!A:F,
  "SELECT B, SUM(D), SUM(E), SUM(D)-SUM(E)
   WHERE A >= date '2026-04-01' AND A <= date '2026-06-30'
   GROUP BY B
   LABEL SUM(D) '売上', SUM(E) '売上原価', SUM(D)-SUM(E) '粗利益'",
  1
)

地域(B列)はディメンションなのでGROUP BYに入れます。売上・売上原価・粗利益はすべてメジャーなので、SUM()でラップするか、集計値どうしの計算式として表現します。SUM(D)-SUM(E) が問題なく動作するのは、両方の要素が集計済みだからです。グループごとに1つの値が返る形になっていれば、QUERYの要件を満たします。

QUERY GROUP BY vs. SUMIFS:使い分けの判断基準

単一シートの集計であれば、QUERY GROUP BYはシンプルで使いやすい選択肢です。しかし複数シートにまたがる財務モデルでは、SUMIFSの方が優位なケースが多くなります。両者を正直に比較してみましょう。

シナリオQUERY GROUP BYSUMIFS / SUMPRODUCT
単一シートの集計SQLライクな構文でシンプルやや記述が長くなる
別シートへの参照ネイティブ非対応ネイティブ対応、安定動作
前提条件シートの動的な日付文字列連結が必要">=" & 前提!$B$3 で直接参照可
複数フィルター条件WHERE句で記述SUMIFSの条件を重ねて記述
1万行超のデータ再計算が遅くなる傾向高速
ディメンションの値が不定自動的に全行を返す値のハードコーディングが必要
グループ化エラーのデバッグcannot group without agg#VALUE!(範囲誤りの場合)

たとえば、四半期の予実管理レポートで、対象期間が「前提条件」シートの $B$3:$B$4 に設定されており、売上データが 'P&L'!C:C にある場合、SUMIFSであれば文字列連結なしにシンプルに書けます。

=SUMIFS(
  'P&L'!C:C,
  'P&L'!B:B, ">=" & 前提条件!$B$3,
  'P&L'!B:B, "<=" & 前提条件!$B$4,
  'P&L'!D:D, 実績!$A2
)

QUERYでは 前提条件!$B$3 をクエリ文字列内に直接参照できません。テキストとして連結する必要があり、日付のフォーマット問題でエラーが起きることがあります。しかもそのエラーが本来の問題とは無関係に見えるため、デバッグに無駄な時間を費やすことになります。SUMIFSはセル参照をそのまま受け取れるので、こうした問題が起きません。

QUERY GROUP BYが本領を発揮する場面

QUERYが真価を発揮するのは、ディメンションの値が事前にわからない場合です。たとえば、現在取り扱っているSKUが14種類あり、粗利益率の分析(目標38.5%、現状の加重平均34.1%)においてSKUごとの貢献利益を確認したい場合を考えましょう。QUERYであれば、SKU名をハードコーディングすることなく14行すべてを返すことができます。

=QUERY(
  '売上'!A:G,
  "SELECT C, SUM(D), SUM(E), (SUM(D)-SUM(E))/SUM(D)
   GROUP BY C
   ORDER BY (SUM(D)-SUM(E))/SUM(D) DESC
   LABEL C 'SKU', SUM(D) '売上', SUM(E) '売上原価'",
  1
)

(SUM(D)-SUM(E))/SUM(D) という利益率の計算式でグループ化エラーが起きないのは、すべての構成要素が集計済みだからです。グループごとに1つの値が確実に返ります。SUMIFSで同じことをするには各SKUを1行ずつハードコードする必要があり、新しいSKUが追加されるたびに修正が必要になります。

QUERYが遅い場合はSUMPRODUCTに切り替える

1万行を超えるP&Lデータや、複数シートを参照する財務モデルでQUERYが再計算のボトルネックになることがあります。このような場合、SUMPRODUCTへの移行が現実的な解決策です。

たとえば、地域別・期間別の売上集計をQUERYで書いていた場合:

=QUERY('売上'!A:E, "SELECT B, SUM(D) WHERE A >= date '2026-04-01' GROUP BY B", 1)

これをSUMPRODUCTに置き換えると:

=SUMPRODUCT(
  ('売上'!A2:A10000 >= 前提条件!$B$3) *
  ('売上'!A2:A10000 <= 前提条件!$B$4) *
  ('売上'!B2:B10000 = $A2) *
  '売上'!D2:D10000
)

SUMPRODUCTはQUERYと異なり、別シート参照をネイティブで扱えます。また、前提条件シートのセルを直接参照できるため、文字列連結や日付フォーマット変換が不要です。再計算速度もQUERYより安定しており、10万行規模のデータでも実用的に動作します。

ただし、SUMPRODUCTはディメンションを自動で列挙する機能を持っていません。地域の値が事前に固定されている予実管理表(例:関東・関西・中部・九州といった固定軸で構成されたP&Lテンプレート)には適しています。一方で、SKUや担当者名など動的に増減するディメンションの全量を出力したい場合は、引き続きQUERYが有効です。

QUERYからSUMPRODUCTへの移行を検討すべきタイミング:

  • スプレッドシートの再計算に3秒以上かかる
  • QUERYが参照するシートが2枚以上ある
  • 前提条件シートのパラメータを頻繁に変更するワークフローである
  • ディメンションの値があらかじめ固定されている

見落としやすい注意点

LABELと誤ったエラー表示: LABEL句自体がグループ化エラーを引き起こすことはありません。しかし、SELECT句にすでに問題がある状態でLABELを記述すると、エラーメッセージがLABEL起因のように見えることがあります。LABELを触る前に、必ずSELECT句とGROUP BYを先に確認してください。

COUNT()の構文: COUNT(D) はD列の非NULL値を数えます。引数なしの COUNT() はGoogle SheetsのQUERYでは無効な構文です。2026年6月現在、この場合のエラーメッセージが cannot group without agg として表示されることがあり、デバッグに余計な時間がかかります。グループ化の設定が正しそうなのにエラーが消えない場合は、COUNT構文を確認してみてください。

計算式の中のグループ化ルール: SUM(D)/COUNT(D) は有効です(両方の要素が集計済み)。SUM(D)/D は無効です(分母のDが集計されていない)。このルールは単独の列参照だけでなく、計算式中のあらゆる項目に適用されます。

ModelMonkeyを使うと、このクラスのQUERYエラーをシートへの書き込み前に検出し、修正済みの数式をサイドバーで直接提案してくれます。複数シートが絡み合ったモデルの深いところでエラーが出ていて、エラーメッセージだけでは問題の列が特定できないときに、特に重宝します。

よくある質問

Q: cannot group without agg エラーとは何ですか?

A: Google SheetsのQUERY関数で、SELECT句に記述した列が集計関数でラップされておらず、かつGROUP BY句にも含まれていない場合に発生するエラーです。SELECT句のすべての列は、GROUP BYに列挙する「ディメンション」か、SUM()などでラップする「メジャー」のどちらかでなければなりません。

Q: QUERY GROUP BYとSUMIFSはどちらを使うべきですか?

A: ディメンションの値が事前にわからない動的な集計(SKUや担当者名が随時追加されるケースなど)にはQUERY GROUP BYが適しています。一方、別シートへの参照、前提条件シートからの動的な日付指定、大量行での高速再計算が必要な場合はSUMIFSまたはSUMPRODUCTの方が安定して動作します。

Q: QUERYで前提条件シートのセルを日付条件に使えますか?

A: 直接参照はできません。QUERYは日付を文字列として連結する必要があり、"WHERE A >= date '" & TEXT(前提条件!$B$3, "yyyy-MM-dd") & "'" のような形式が必要です。フォーマット変換のミスがエラーの原因になりやすいため、動的な日付参照が多い場合はSUMIFSへの移行を検討してください。

Q: QUERYが遅い場合はどうすればよいですか?

A: 1万行超のデータでQUERYが遅い場合は、SUMPRODUCTへの移行を検討してください。SUMPRODUCTは別シート参照をネイティブで扱えるうえ、再計算速度が安定しています。ただし、ディメンションの値が事前に固定されていることが前提です。動的にディメンションを列挙したい場合はQUERYが引き続き有効です。

Q: SUM(D)-SUM(E) はなぜ cannot group without agg エラーにならないのですか?

A: 式の構成要素がすべて集計済みだからです。SUM(D)とSUM(E)はそれぞれ1グループにつき1つの値を返すため、その差分もグループごとに1つの値になります。SUM(D)-E のように非集計の列が混在するとエラーになります。

まとめ

cannot group without agg エラーは、SELECT句に記述した1つ以上の列が、集計関数でラップされておらずGROUP BYにも含まれていないことを意味します。対処法はシンプルです。SELECT句の全列を確認し、ディメンションはGROUP BYに、メジャーはSUM・AVG・COUNT・MAX・MINでラップする。

別シート参照や前提条件シートからの動的な日付指定が必要な場合はSUMIFSが適しています。1万行超のデータで再計算が遅い場合はSUMPRODUCTへの移行を検討してください。逆に、ディメンションの値が事前にわからない動的な集計にはQUERYのGROUP BYがより適切な選択です。ツール選択の基準は、構文の好みではなく、モデルの設計要件に基づいて判断してください。