SQLのGROUP BYを基本から理解する|集計ミスを防ぐ確認ポイント
この記事でわかること
SQLのGROUP BYは、同じ値を持つ行をまとめて、件数や合計などを出すための集計構文です。
たとえば、売上一覧から商品ごとの売上合計を出したり、社員一覧から部署ごとの人数を数えたりするときに使います。
この記事では、GROUP BYの基本構文だけでなく、SELECT句に書ける列の考え方、WHEREとHAVINGの違い、集計結果がずれる原因まで順番に整理します。
GROUP BYは構文だけを見ると短く見えますが、実際には「どの単位で集計するのか」を正しく決めることがとても重要です。
特に大切なのは、GROUP BYを書く前に「何を1行として集計したいのか」を言葉で決めることです。
集計単位があいまいなまま列を追加したりJOINしたりすると、SQL自体は動いても、期待と違う件数や合計になることがあります。
また、COUNT(*)とCOUNT(列名)の違いや、NULLの扱いを知らないまま集計すると、結果を見たときに「なぜ数が合わないのか」と迷いやすくなります。
この記事では、初心者がつまずきやすいポイントを、構文の暗記ではなく確認手順として理解できるように説明します。
読み終えるころには、GROUP BYの文法だけでなく、集計ミスを見つけるための確認順もつかめるはずです。
SQLのGROUP BYとは何をする構文か
GROUP BYは、指定した列の値が同じ行を1つのグループにまとめ、そのグループごとに集計結果を出すために使います。
たとえば売上一覧の明細データがある場合、商品名ごとに売上合計を出したり、部署ごとに社員数を数えたりするときに使います。
明細データには、1件の注文、1人の社員、1回のアクセスなど、元のデータが1行ずつ並んでいます。
GROUP BYを使うと、その明細行をそのまま表示するのではなく、指定した単位でまとめた結果として表示できます。
GROUP BYを使わないSELECT文は、基本的に1行ごとの明細をそのまま見るためのものです。
一方でGROUP BYを使うと、複数の明細行をまとめた集計結果として見ることができます。
たとえば、売上テーブルに同じ商品名の行が何行もある場合でも、商品名でGROUP BYすれば商品ごとの1行にまとめられます。
そのうえでSUMを使えば商品ごとの売上合計になり、COUNTを使えば商品ごとの販売件数になります。
つまりGROUP BYは、細かい明細データを、分析しやすい集計データに変えるための入り口です。
GROUP BYは集計単位を決めるために使う
GROUP BYで最初に考えるべきことは、どの列を基準にして行をまとめたいかです。
商品別に見たいなら商品名をGROUP BYし、部署別に見たいなら部署名をGROUP BYし、月別に見たいなら年月をGROUP BYします。
この「何別で見るか」が集計単位です。
集計単位は、SQLを書く前に決めておくと迷いにくくなります。
たとえば「商品別の売上合計を見たい」と言えるなら、GROUP BYに入れる中心の列は商品名です。
たとえば「部署と役職別の人数を見たい」と言えるなら、GROUP BYに入れる列は部署名と役職です。
このように、集計したい内容を言葉にすると、GROUP BYに必要な列が自然に見えてきます。
集計単位を決めずにSQLを書き始めると、あとからSELECT句に列を足したりGROUP BYに列を増やしたりして、結果が細かく分かれすぎることがあります。
GROUP BYと集計関数の関係
GROUP BYは、COUNT、SUM、AVG、MAX、MINなどの集計関数と一緒に使うことが多いです。
COUNTは件数、SUMは合計、AVGは平均、MAXは最大値、MINは最小値を求めるときに使います。
たとえば商品ごとの売上合計を出したい場合は、商品名でグループ化し、それぞれのグループの金額をSUMで合計する考え方になります。
部署ごとの人数を出したい場合は、部署名でグループ化し、それぞれの部署に含まれる行数をCOUNTで数えます。
月ごとの平均購入額を出したい場合は、月でグループ化し、それぞれの月の購入額をAVGで平均します。
このように、GROUP BYだけでは合計や件数は計算されません。
GROUP BYは「まとめる単位」を決め、集計関数は「まとめた後に何を計算するか」を決める役割です。
GROUP BYと集計関数を分けて考えると、SQL全体の意味を読み取りやすくなります。
GROUP BYの基本構文と読み方
GROUP BYの基本は、SELECT句で表示したいグループ列と集計結果を書き、FROM句で対象テーブルを指定し、GROUP BY句でまとめる列を指定する形です。
商品ごとの売上合計なら、SELECT 商品名, SUM(金額) AS 売上合計 FROM 売上 GROUP BY 商品名 のように読みます。
このSQLは、売上テーブルを商品名ごとにまとめ、それぞれの金額を合計し、商品名と売上合計を表示するという意味です。
SQLは上から書きますが、考える順番としては、対象データを決め、必要なら行を絞り、グループ化し、集計し、必要なら並び替える流れで理解すると分かりやすくなります。
基本構文を読むときは、SELECT句だけを見るのではなく、GROUP BY句とセットで確認することが大切です。
SELECT句に商品名があり、GROUP BY句にも商品名があるなら、商品名ごとの集計だと判断できます。
SELECT句にSUM(金額)があるなら、各商品グループの金額を合計していると判断できます。
このように、SELECT句とGROUP BY句を一緒に読むと、SQLが何を集計しているのかを理解しやすくなります。
GROUP BYの近くには、WHERE、HAVING、ORDER BYが一緒に出てくることもあります。
WHEREは集計前の行を絞り、HAVINGは集計後の結果を絞り、ORDER BYは最後に表示順を整えるために使います。
まずは1列でグループ化する
初心者は、まず1列だけをGROUP BYする例から理解するとつまずきにくいです。
たとえば部署ごとの人数を知りたい場合は、部署名でグループ化し、それぞれの部署に何行あるかをCOUNTで数えます。
商品ごとの売上合計を知りたい場合は、商品名でグループ化し、それぞれの商品の金額をSUMで合計します。
1列GROUP BYでは、結果の1行が「1つの商品」「1つの部署」「1つの日付」などに対応すると考えると理解しやすいです。
たとえば商品Aの明細が5行あり、商品Bの明細が3行ある場合、商品名でGROUP BYすると商品Aの行と商品Bの行にまとまります。
このときCOUNT(*)を使えば商品Aは5件、商品Bは3件として表示できます。
このときSUM(金額)を使えば、商品Aの5行分の金額合計と、商品Bの3行分の金額合計を表示できます。
まずは1列のグループ化で、明細行が集計行に変わる感覚をつかむことが大切です。
集計結果の列名を見やすくする
集計関数を使うと、結果の列名がSUM(金額)やCOUNT(*)のように表示されることがあります。
意味は分かっても、集計結果を表として見たり他の人に共有したりするときは、列名が読みづらくなることがあります。
そのため、ASを使って売上合計や件数のような別名を付けると、結果の意味が分かりやすくなります。
たとえばSUM(金額) AS 売上合計と書けば、結果の列名を売上合計として表示できます。
たとえばCOUNT(*) AS 注文件数と書けば、何を数えた列なのかを見ただけで判断しやすくなります。
列名を見やすくすることは、SQLの正しさそのものではありませんが、集計結果を確認するときのミスを減らす助けになります。
特に複数の集計値を並べる場合は、売上合計、平均単価、件数などの名前を付けると結果を読み違えにくくなります。
あとからSQLを見直すときも、別名が付いているほうが処理の意図を思い出しやすくなります。
SELECT句に書ける列とGROUP BYのルール
GROUP BYでよくつまずくのが、SELECT句にどの列を書けるのかというルールです。
基本的には、GROUP BYに指定した列、または集計関数で計算した列をSELECT句に書けます。
判断基準は、その列の値が1つのグループの中で1つに決まるかどうかです。
この考え方を理解すると、なぜ自由に列を追加できないのかが分かりやすくなります。
GROUP BYは複数行を1行にまとめる処理なので、まとめた後に表示できる値には制約があります。
たとえば部署ごとに人数を数える場合、部署名はグループの単位なので結果の1行につき1つに決まります。
しかし社員名は1つの部署の中に複数存在するため、結果の1行にどの社員名を出すべきか決まりません。
この「どれを出せばよいか決まらない」という状態が、SELECT句で自由に列を書けない理由です。
GROUP BYに書いた列はSELECT句に書ける
GROUP BYに書いた列は、その列の値ごとにグループを作るため、集計結果の1行につき値が1つに決まります。
たとえば商品名でGROUP BYした場合、結果の各行は1つの商品名に対応します。
そのため、SELECT句に商品名を書くことは自然です。
同じように、部署名でGROUP BYしたなら部署名を表示でき、年月でGROUP BYしたなら年月を表示できます。
複数列でGROUP BYした場合も同じ考え方です。
部署名と役職でGROUP BYしたなら、結果の各行は部署名と役職の組み合わせに対応します。
そのため、SELECT句には部署名と役職を表示できます。
GROUP BYに書いた列は、集計結果の「見出し」や「分類名」として表示される列だと考えると分かりやすいです。
集計していない列をそのまま書くとエラーになりやすい
GROUP BYに書いていない列をSELECT句にそのまま書くと、エラーになったり、DB製品によっては分かりにくい結果になったりすることがあります。
理由は、1つのグループの中に複数の値が入っている可能性があるからです。
たとえば部署ごとに人数を数えているのに、SELECT句に社員名をそのまま書くと、1つの部署に複数の社員名があるため、どの社員名を表示すればよいのか決められません。
表示したい列があるなら、その列をGROUP BYに含めるのか、MAXやMINなどで1つの値に集計するのかを考える必要があります。
ただし、エラーを消すためだけにGROUP BYへ列を追加するのは注意が必要です。
社員名をGROUP BYに追加すればエラーは消えるかもしれませんが、結果は部署ごとの人数ではなく、部署と社員名ごとの人数になります。
つまり、列を追加すると集計単位そのものが変わります。
SELECT句のエラーを直すときは、目的の集計単位を変えずに直せているかを必ず確認しましょう。
複数列でGROUP BYするときの考え方
GROUP BYは1列だけでなく、複数列を指定して使うこともできます。
複数列でGROUP BYすると、指定した列の組み合わせごとに1つのグループが作られます。
たとえば部署名と役職でGROUP BYすると、営業部の一般社員、営業部の管理職、開発部の一般社員のように、部署と役職の組み合わせごとに集計されます。
便利な一方で、列を増やすほど集計単位が細かくなるため、結果が想定より分かれて見えることがあります。
複数列GROUP BYは、より詳しい内訳を見たいときに向いています。
たとえば月別売上だけでなく、月別かつ商品カテゴリ別に売上を見たい場合は、月と商品カテゴリをGROUP BYします。
たとえば部署別人数だけでなく、部署別かつ雇用形態別に人数を見たい場合は、部署名と雇用形態をGROUP BYします。
このように複数列GROUP BYは、集計結果を細かく分解して原因や傾向を見たいときに役立ちます。
ただし、細かく見たい目的がない列まで入れると、集計結果の読み取りが難しくなります。
1列集計と複数列集計の違い
1列集計では、結果の1行は1つの列の値に対応します。
部署名だけでGROUP BYすれば、部署ごとの人数や売上を確認できます。
複数列集計では、結果の1行は複数列の組み合わせに対応します。
部署名と役職でGROUP BYすれば、部署ごとの合計ではなく、部署と役職の組み合わせごとの合計になります。
この違いは、結果の行数にも表れます。
部署が3種類だけなら、部署名だけのGROUP BYでは基本的に3行の結果になります。
しかし部署ごとに役職が複数あるなら、部署名と役職のGROUP BYでは結果の行数が増えます。
行数が増えること自体は間違いではありません。
大切なのは、見たい集計単位と結果の粒度が合っているかを確認することです。
列を増やしすぎると結果が分かれすぎる
GROUP BYに列を追加すると、集計結果の粒度は細かくなります。
これは便利な反面、不要な列までGROUP BYに入れると、見たい合計よりも細かい単位で結果が分かれます。
たとえば商品別売上を見たいだけなのに、販売日や担当者までGROUP BYに入れると、商品別ではなく商品と日付と担当者の組み合わせ別になります。
その結果、同じ商品が何行にも分かれ、商品別の合計が見えにくくなります。
集計結果が想定より多いときは、GROUP BYに入れた列が本当に必要かを確認することが大切です。
特に、SELECT句のエラーを解消するためだけにGROUP BYへ列を追加した場合は注意が必要です。
エラーは消えても、集計単位が変わってしまうと、欲しかった結果から離れてしまいます。
GROUP BYに列を増やすときは、「その列で結果を分けたいのか」を必ず確認しましょう。
WHEREとHAVINGの違い
WHEREとHAVINGはどちらも条件で絞り込むために使いますが、絞り込むタイミングが違います。
WHEREは集計する前の明細行を絞り込み、HAVINGはGROUP BYで集計した後の結果を絞り込みます。
この違いを理解していないと、条件を書く場所を間違えて、欲しい集計結果にならないことがあります。
たとえば、2026年1月の売上だけを集計したいなら、売上日の条件は集計前にかける必要があります。
この場合は、元の売上行を絞る条件なのでWHEREを使います。
一方で、商品ごとの売上合計を出したあと、売上合計が10万円以上の商品だけを見たいなら、集計後の値に条件をかける必要があります。
この場合は、SUMで計算した結果に対する条件なのでHAVINGを使います。
つまり、元データの列で絞るならWHERE、集計結果で絞るならHAVINGと考えると判断しやすくなります。
| 比較項目 | WHERE | HAVING |
|---|---|---|
| 条件をかけるタイミング | 集計前 | 集計後 |
| 主な対象 | 元データの列 | 集計結果 |
| よく使う条件 | 売上日、カテゴリ、部署、ステータスなど | SUM、COUNT、AVGなどの結果 |
| 例 | 売上日が2026年1月の行だけにする | 売上合計が10万円以上のグループだけにする |
WHEREは集計する前の行を絞り込む
WHEREは、GROUP BYでまとめる前に対象行を減らすために使います。
たとえば2026年1月の売上だけを集計したい場合は、売上日が2026年1月に該当する行だけをWHEREで絞ります。
カテゴリが文房具の商品だけを集計したい場合も、カテゴリに対する条件はWHEREに書くのが自然です。
WHEREは、まだ集計されていない元データに対する条件だと考えると判断しやすくなります。
WHEREで対象行を絞ると、GROUP BYに渡される行数そのものが減ります。
そのため、WHEREの条件を間違えると、そもそも集計対象になるデータが変わります。
たとえば売上日を間違えて絞ると、GROUP BYやSUMが正しくても、集計対象の期間がずれます。
集計結果がおかしいときは、GROUP BYだけでなく、WHEREで対象データを絞りすぎていないかも確認しましょう。
HAVINGは集計した後の結果を絞り込む
HAVINGは、GROUP BYでまとめた後の集計結果に条件をかけるために使います。
たとえば商品ごとの売上合計を出したうえで、売上合計が10万円以上の商品だけを表示したい場合はHAVINGを使います。
件数が5件以上の部署だけを表示したい場合も、COUNTした結果に条件をかけるためHAVINGを使います。
集計関数の結果を条件にしたいときは、WHEREではなくHAVINGを使うと覚えると分かりやすいです。
HAVINGは集計後のグループに対する条件なので、COUNT(*)やSUM(金額)のような集計結果を使えます。
たとえばCOUNT(*) >= 10 のような条件は、各グループの件数が計算された後でなければ判断できません。
そのため、このような条件はWHEREではなくHAVINGに書く必要があります。
WHEREとHAVINGを迷ったら、条件に使いたい値が集計前から存在する列なのか、集計後に計算される値なのかを確認しましょう。
GROUP BYで集計ミスが起きる主な原因
GROUP BYの構文が正しくても、集計結果が期待とずれることはあります。
よくある原因は、GROUP BYする列が多すぎること、JOINによって行数が増えていること、NULLの扱いを見落としていること、COUNTの種類を混同していることです。
結果がずれたときは、SQL全体を一度に疑うのではなく、原因を分けて確認すると修正しやすくなります。
特に実務では、SQLがエラーにならないのに数値だけが合わないケースがよくあります。
この場合、文法ミスではなく、集計対象や集計単位のズレが原因になっていることがあります。
また、JOINした後の行数が増えていることに気づかず、そのままSUMやCOUNTをしてしまうこともあります。
GROUP BYの結果がおかしいと感じたら、まずは「どの段階で行数や集計単位が変わったのか」を確認しましょう。
原因を切り分けるときは、GROUP BY句だけを見るのではなく、FROM、JOIN、WHERE、SELECT、HAVINGまで含めて確認することが大切です。
GROUP BYする列が多すぎる
GROUP BYに列を増やすと、集計単位が細かくなります。
商品別の売上を見たいだけなら商品名だけで十分ですが、そこに日付や担当者を加えると、商品別ではなく商品と日付と担当者の組み合わせ別になります。
その結果、同じ商品が複数行に分かれて表示され、合計が合っていないように見えることがあります。
実際には合計が間違っているのではなく、見たい単位より細かく分かれているだけの場合があります。
たとえば商品Aの合計を1行で見たいのに、商品Aが日付別に10行出ていると、商品別合計が見えなくなります。
この場合は、GROUP BYから日付を外すか、別の集計段階を作る必要があります。
まずは「何別で集計したいのか」を言葉で確認し、その言葉に必要な列だけをGROUP BYに入れましょう。
GROUP BYの列を増やす前には、その列で結果を分けたい理由があるかを確認しましょう。
JOINで行数が増えている
複数のテーブルをJOINしてからGROUP BYすると、結合の仕方によって行数が増えることがあります。
特に1対多の関係でJOINすると、元の1行が複数行に増え、SUMやCOUNTが想定より大きくなることがあります。
たとえば注文テーブルと注文明細テーブルをJOINすると、1つの注文が複数の明細行に展開されることがあります。
この状態で注文単位の金額をそのままSUMすると、同じ注文金額を複数回足してしまう可能性があります。
結合条件が不足している場合も、意図しない組み合わせが増えて集計結果が膨らみます。
GROUP BYの結果がおかしいときは、集計前にJOIN後の行数を確認し、結合条件が正しいかを見ることが大切です。
JOINを使うSQLでは、GROUP BYの前にSELECT COUNT(*)で行数を確認するだけでも原因を見つけやすくなります。
また、主キーや外部キーの関係を意識して、1対1で結合されるのか、1対多で結合されるのかを確認しておきましょう。
NULLを見落としている
NULLは空文字や0とは違い、値が存在しない状態を表します。
GROUP BYでは、NULLが1つのグループとしてまとまることがあります。
そのため、部署名がNULLの行があると、部署名が空に見える集計行が出ることがあります。
この行を見落とすと、部署別の合計件数を確認したときに違和感が出ることがあります。
また、COUNT(列名)はその列がNULLの行を数えないため、COUNT(*)と結果が違うことがあります。
たとえば10行のデータがあっても、メールアドレス列がNULLの行が2行あれば、COUNT(メールアドレス)は8になります。
NULLを含む可能性がある列を集計するときは、NULLを別グループとして扱ってよいのかを考える必要があります。
必要に応じて、NULLを「未設定」などの表示に置き換えて確認すると、集計結果を読み取りやすくなります。
COUNT(*)とCOUNT(列名)を混同している
COUNT(*)は、条件に合った行数を数えます。
一方でCOUNT(列名)は、その列がNULLではない行数を数えます。
たとえば10行あるテーブルで、メールアドレスが入っている行が8行だけなら、COUNT(*)は10になり、COUNT(メールアドレス)は8になります。
この違いを知らないと、同じデータを数えているつもりでも、結果が違って見えます。
単純にレコード数を知りたいときはCOUNT(*)を使うのが分かりやすいです。
特定の列に値が入っている件数を知りたいときはCOUNT(列名)を使います。
件数が合わないと感じたときは、何の件数を数えたいのかを確認してからCOUNTの書き方を選びましょう。
COUNTの違いはNULLの扱いとつながっているため、NULLを含む列を数えるときは特に注意が必要です。
集計結果がずれたときの確認手順
GROUP BYの結果が期待と違うときは、思いついた場所から直すよりも、確認する順番を決めたほうが原因を見つけやすくなります。
おすすめの順番は、集計単位を確認し、JOIN前後の件数を見て、SELECT句とGROUP BY句を照らし合わせることです。
その後で、WHEREとHAVINGの位置、NULLの有無、COUNTの種類を確認すると、原因を切り分けやすくなります。
集計ミスは、1つの原因だけで起きるとは限りません。
たとえば、GROUP BYの列が多すぎるうえに、JOIN後の行数も増えている場合があります。
そのため、結果だけを見てすぐにGROUP BYを直すのではなく、元データから集計結果までの流れを順番に追うことが大切です。
確認するときは、最終的なSQLを少しずつ分解して、FROMとJOINだけの結果、WHEREまでかけた結果、GROUP BY後の結果を段階的に見ると分かりやすくなります。
このように段階ごとに確認すると、どこで想定と違う状態になったのかを見つけやすくなります。
まず集計したい単位を言葉で確認する
最初に、「商品別の売上合計を見たい」「月別の注文件数を見たい」「部署と役職別の人数を見たい」のように、集計したい単位を言葉で確認します。
この言葉に出てこない列がGROUP BYに入っている場合、その列が結果を細かく分けている可能性があります。
逆に、言葉に出てくる列がGROUP BYに入っていない場合、欲しい単位で集計できていない可能性があります。
SQLを書く前でも書いた後でも、集計単位を言葉に戻す確認は効果的です。
たとえば「商品別」と言っているのにGROUP BYに商品名と販売日が入っているなら、日付別に分かれている可能性があります。
たとえば「月別」と言っているのにGROUP BYに日付そのものが入っているなら、月別ではなく日別になっている可能性があります。
言葉で確認する作業は単純ですが、GROUP BYのミスを見つけるうえでかなり役立ちます。
集計結果がずれたときは、SQLの前にまず目的の日本語を確認してみましょう。
JOIN前後の件数を確認する
JOINを使っている場合は、GROUP BYの前に行数が増えていないかを確認します。
元テーブルでは100行だったのに、JOIN後に150行になっているなら、その増えた50行が集計結果に影響している可能性があります。
1対多のJOINが必要なケースもありますが、その場合は増えた行数を前提に集計してよいのかを確認する必要があります。
JOIN後の行数を見るだけでも、GROUP BYではなく結合条件が原因だったと分かることがあります。
特に、結合条件に必要な列が不足していると、想定より多くの組み合わせが作られることがあります。
また、同じキーに対して結合先テーブルに複数行が存在する場合も、元の行が増えます。
この状態でSUMを使うと、同じ金額を複数回足してしまうことがあります。
JOINを含む集計SQLでは、いきなりGROUP BY後の結果を見るのではなく、JOIN直後の件数を確認しましょう。
SELECT句とGROUP BY句を照らし合わせる
GROUP BYを使うときは、SELECT句に書いた列がGROUP BYに含まれているか、または集計関数で処理されているかを確認します。
SELECT句に集計していない列が混ざっている場合、その列をGROUP BYに含めるべきか、集計関数で1つにまとめるべきかを考えます。
ただし、エラーを消すためだけにGROUP BYへ列を追加すると、集計単位が変わってしまうことがあります。
エラーを直す前に、その列を表示する必要が本当にあるのかを確認しましょう。
たとえば部署ごとの人数を見たいだけなら、社員名を表示する必要はないかもしれません。
一方で、部署と役職ごとの人数を見たいなら、役職はGROUP BYに含める必要があります。
この判断は、単にSQLの文法に合わせるというより、集計結果として何を見たいかに合わせて行います。
SELECT句とGROUP BY句を照らし合わせるときは、「表示したい列」と「集計単位を決める列」を混同しないことが大切です。
GROUP BYを書くときのチェックリスト
GROUP BYは、書く前と書いた後で確認するポイントを分けるとミスを減らせます。
書く前は集計の目的を整理し、書いた後は結果の件数や集計単位が想定と合っているかを確認します。
慣れていないうちは、構文を先に書くよりも、チェックリストに沿って考えたほうが安定します。
特に、集計単位、対象データ、JOINの有無、NULLの扱いは結果に大きく影響します。
GROUP BYで迷ったときは、次のような観点で確認してみましょう。
書く前と書いた後の両方で確認すると、集計単位のズレや件数の違和感に気づきやすくなります。
| タイミング | 確認すること |
|---|---|
| 書く前 | 何別に集計したいかを言葉で決める |
| 書く前 | 必要な集計関数がCOUNT、SUM、AVG、MAX、MINのどれか確認する |
| 書く前 | WHEREで絞る条件とHAVINGで絞る条件を分ける |
| 書く前 | JOINが必要かどうかを確認する |
| 書く前 | NULLを含む可能性がある列を確認する |
| 書いた後 | 結果の行数が想定より多すぎないか確認する |
| 書いた後 | JOIN後に行数が増えていないか確認する |
| 書いた後 | SELECT句とGROUP BY句の対応を確認する |
| 書いた後 | NULLやCOUNTの違いが結果に影響していないか確認する |
| 書いた後 | ORDER BYで見やすい順番にできているか確認する |
書く前に確認すること
SQLを書く前に、まず集計単位を決めます。
次に、集計したい値が件数なのか、合計なのか、平均なのかを決めます。
さらに、対象データをどこで絞るのかも考えておくと、WHEREとHAVINGを混同しにくくなります。
たとえば、特定期間の売上だけを商品別に集計したいなら、期間条件はWHEREで先に絞ります。
たとえば、商品別に集計した後で売上合計が一定以上の商品だけを見たいなら、その条件はHAVINGで考えます。
JOINが必要な場合は、結合した結果の行数が増える可能性も先に意識しておきます。
NULLを含む列を数える場合は、COUNT(*)を使うのかCOUNT(列名)を使うのかも決めておきます。
この準備をしておくと、SELECT句に何を書けばよいかも自然に決まりやすくなります。
書いた後に確認すること
SQLを書いた後は、結果の行数と集計単位を見ます。
想定より行数が多い場合は、GROUP BYの列が多すぎないか、JOINで行数が増えていないかを確認します。
件数が合わない場合は、COUNT(*)とCOUNT(列名)の違いや、NULLの有無を確認します。
合計が大きすぎる場合は、JOINで同じ金額を複数回足していないかを確認します。
結果が少なすぎる場合は、WHEREで対象データを絞りすぎていないかを確認します。
HAVINGを使っている場合は、集計後の条件で必要なグループまで除外していないかも見直します。
最後にORDER BYで並び順を整えると、結果の確認もしやすくなります。
集計SQLは、書いて終わりではなく、結果を見て意図どおりの単位になっているかを確認することが大切です。
よくある質問
GROUP BYは基本構文だけを見ると単純ですが、SELECT句、ORDER BY、COUNT、NULLなどと組み合わせると迷いやすくなります。
ここでは、初心者が疑問に感じやすい点を短く整理します。
本文で説明した内容と重なる部分もありますが、よくあるつまずきとして再確認しておくと理解が定着しやすくなります。
特に、GROUP BYとORDER BYの違い、SELECT句に自由に列を書けない理由、COUNTの違いは実務でもよく出てくる疑問です。
迷いやすい用語は、役割の違いを短く整理しておくとSQLを読み返すときにも役立ちます。
GROUP BYとORDER BYは何が違いますか?
GROUP BYは、同じ値を持つ行をまとめて集計するための句です。
ORDER BYは、取得した結果の並び順を変えるための句です。
つまり、GROUP BYは結果の行の作り方に関わり、ORDER BYは作られた結果の見せ方に関わります。
集計してから売上合計の大きい順に並べたい場合は、GROUP BYとORDER BYを組み合わせて使います。
たとえば商品ごとの売上合計を出す処理はGROUP BYが担当します。
その売上合計を多い順に並べる処理はORDER BYが担当します。
GROUP BYを書いたからといって、必ず見やすい順番で結果が返るとは限りません。
順番を指定したい場合は、GROUP BYとは別にORDER BYを書きましょう。
GROUP BYを使うとSELECT句に自由に列を書けないのはなぜですか?
GROUP BYを使うと、複数の明細行が1つの集計行にまとまります。
そのとき、グループ内で値が1つに決まらない列をSELECT句に書くと、どの値を表示すればよいのか判断できません。
そのため、SELECT句にはGROUP BYに指定した列か、集計関数で1つにまとめた値を書くのが基本です。
自由に列を追加したいときほど、その列が集計単位を変えてしまわないか確認しましょう。
たとえば部署ごとの人数を集計しているときに社員名を表示しようとすると、1つの部署に複数の社員名があるため表示値が決まりません。
この場合、社員名をGROUP BYに入れると部署別ではなく部署と社員名別の集計になります。
目的が部署別集計なら、社員名を表示しないほうが自然です。
このように、SELECT句に列を足すときは、文法だけでなく集計目的との整合性も確認しましょう。
COUNT(*)とCOUNT(列名)は同じですか?
COUNT(*)とCOUNT(列名)は同じではありません。
COUNT(*)は行数を数え、COUNT(列名)はその列がNULLではない行数を数えます。
NULLを含む列でCOUNT(列名)を使うと、実際の行数より少なく見えることがあります。
単純に行数を知りたいならCOUNT(*)、値が入っている件数を知りたいならCOUNT(列名)を使います。
たとえば会員テーブルで全会員数を知りたいならCOUNT(*)が向いています。
たとえばメールアドレスを登録している会員数を知りたいならCOUNT(メールアドレス)が向いています。
どちらも件数を数える関数ですが、何を件数として数えるのかが違います。
集計結果が合わないと感じたときは、COUNTの対象列にNULLが含まれていないかを確認しましょう。
GROUP BYは必ずORDER BYと一緒に使いますか?
GROUP BYは必ずORDER BYと一緒に使う必要はありません。
GROUP BYは集計単位を作るための句で、ORDER BYは結果を並び替えるための句です。
集計結果の順番が重要ではない場合は、ORDER BYを書かなくても目的を満たせます。
ただし、売上合計の多い順や件数の少ない順で確認したい場合は、ORDER BYを組み合わせると結果を読みやすくできます。
たとえば商品別売上を出したあと、売上合計が大きい商品から確認したいならORDER BY 売上合計 DESCのような考え方になります。
一方で、単に集計結果を取得するだけならORDER BYは必須ではありません。
GROUP BYとORDER BYは名前が似ていて一緒に使われることもありますが、役割は別です。
混同しないためには、GROUP BYはまとめる、ORDER BYは並べると覚えると分かりやすいです。
まとめ
SQLのGROUP BYは、同じ値を持つ行をまとめ、グループごとに件数や合計などを出すための構文です。
基本は、何を1行として集計したいのかを先に決め、その集計単位をGROUP BYに書くことです。
商品別、部署別、月別のように「何別で見たいか」を言葉にすると、GROUP BYに入れる列を判断しやすくなります。
SELECT句には、GROUP BYに指定した列か、COUNTやSUMなどで集計した値を書くのが基本です。
集計していない列をそのままSELECT句に書くと、どの値を表示すべきか決まらないため、エラーや意図しない結果につながりやすくなります。
WHEREは集計前の行を絞り、HAVINGは集計後の結果を絞るために使います。
WHEREとHAVINGで迷ったときは、条件をかけたい値が元データの列なのか、集計後に計算される値なのかを確認しましょう。
集計結果がずれるときは、GROUP BYする列が多すぎないか、JOINで行数が増えていないか、NULLやCOUNTの違いを見落としていないかを確認しましょう。
特にJOINを含むSQLでは、GROUP BYの前に行数が増えていないかを確認することが重要です。
また、COUNT(*)は行数を数え、COUNT(列名)はNULLではない値を数えるため、同じ件数にならないことがあります。
GROUP BYは文法だけを覚えるよりも、集計単位を決める構文として理解すると、実務でも迷いにくくなります。
結果が合わないときは、集計単位、SELECT句、JOIN、WHERE/HAVING、NULL、COUNTの順に確認していくと、原因を切り分けやすくなります。