【AlmaLinux 9】MySQL初心者講座 第6回:集計関数とGROUP BYでデータを「統計」に変える

こんにちは!「LINUX工房」管理人の「リナックス先生」です。
「MySQL初心者講座」もいよいよ第6回。前回は「JOIN」を使って、複数のテーブルを結合する方法を学びました。これによって、私たちは「1行1行のデータ」を非常に詳細に取得できるようになりました。

しかし、実際の現場(実務)では、10万行のデータをそのまま画面に表示することはありません。上司やクライアントが知りたいのは、「結局、合計でいくら売れたのか?」「どのカテゴリが一番人気なのか?」というデータの要約(サマリー)です。

今日学習する「集計関数」と「GROUP BY」をマスターすれば、あなたは単なる「データの入力係」から、データを価値ある情報に変える「データアナリスト」への第一歩を踏み出すことになります。

コウ君

先生、これまでは特定の1人を探したり、一覧を出したりしてきましたが、今回は「計算」がメインなんですね!
「売上の合計」とかはExcelでもよくやりますが、SQLでやると何が違うんですか? 数万件のデータを計算させたら、サーバーが爆発したりしませんか……?

リナックス先生

いい質問ね、コウ君。Excelは数万件の計算をしようとすると動作が重くなるけれど、MySQLは数百万、数千万件のデータを一瞬で計算するために設計されているのよ。
それに、SQLなら「カテゴリごとの平均」や「月ごとの推移」といった複雑な分析も、たった数行のクエリで完了するわ。
今回も、AIを「分析のアドバイザー」として使いながら、プロレベルの集計クエリを身につけていきましょう!

📚 MySQL初心者講座・連載ロードマップ(全8回)


1. データを「1つの数字」に凝縮する!集計関数の基本

集計関数とは、複数の行(レコード)を対象に計算を行い、その結果として「1つの値」を返す関数のことです。MySQLで最もよく使われる5つの集計関数をマスターしましょう。

1-1. COUNT関数:行数を数える

「ユーザーは何人いるか?」「今日の注文は何件か?」など、数を数えるときに使います。

-- 全行数を数える
SELECT COUNT(*) FROM users;

-- 特定のカラム(NULLを除外)を数える
SELECT COUNT(email) FROM users;

COUNT(*) は「行そのもの」を数えますが、COUNT(カラム名) はそのカラムに値が入っている(NULLではない)行だけを数えます。用途に応じて使い分けましょう。

1-2. SUM関数:合計を出す

数値カラムの合計値を算出します。

-- 売上テーブルの合計金額を算出
SELECT SUM(amount) FROM orders;

1-3. AVG関数:平均を出す

数値カラムの平均値を算出します。

-- ユーザーの平均年齢を算出
SELECT AVG(age) FROM users;

1-4. MAX関数・MIN関数:最大・最小を見つける

数値だけでなく、日付や文字列でも使用可能です。

-- 最高価格と最低価格を取得
SELECT MAX(price), MIN(price) FROM products;

-- 最新のログイン日時を取得
SELECT MAX(last_login) FROM users;

2. カテゴリ別に集約する魔法:GROUP BY句の使い方

集計関数の真の力は、GROUP BY句と組み合わせたときに発揮されます。これは「特定のカラムの値が同じものをグループとしてまとめる」という機能です。

2-1. GROUP BYの基本構造

「都道府県ごとにユーザー数を数える」というクエリを見てみましょう。

SELECT
    prefecture,
    COUNT(*) AS user_count
FROM
    users
GROUP BY
    prefecture;

2-2. GROUP BYの動作イメージ

内部的には、MySQLは以下のような手順で処理を行っています。

  1. グループ分け: 指定したカラム(prefecture)の値が同じ行を一箇所に集める。
  2. 集計実行: 分けられた各グループの中で COUNT(*) を実行する。
  3. 出力: グループ名とその計算結果を1行ずつ表示する。

2-3. 注意!GROUP BYで「やってはいけない」こと

初心者が最もハマるエラーがあります。それは、「SELECT句に、グループ化に使っていないカラムを単独で書いてしまうこと」です。

-- エラー(または予期せぬ結果)になる例
SELECT
    prefecture,
    name,       -- これがNG!グループ内でどの「名前」を出すべきかMySQLが判断できない
    COUNT(*)
FROM
    users
GROUP BY
    prefecture;

SELECTに含めて良いのは、「GROUP BYに指定したカラム」か「集計関数を通したカラム」のどちらかだけです。この原則はプロのエンジニアでも稀に忘れることがあるので、肝に銘じておきましょう。


3. 集計後のデータをフィルタリングする「HAVING句」

第4回で「抽出条件はWHERE句」と学びましたが、集計後の数値に対して条件をつけたい場合は HAVING句 を使います。

3-1. HAVING句が必要なシーン

「ユーザーが10人以上いる都道府県だけを表示したい」という場合、COUNT(*) の結果に対して条件を指定する必要があります。

SELECT
    prefecture,
    COUNT(*) AS user_count
FROM
    users
GROUP BY
    prefecture
HAVING
    user_count >= 10;

3-2. HAVINGとWHEREの併用

WHEREとHAVINGは同時に使うことができます。

-- 「20歳以上のユーザー」に絞り込み、かつ「10人以上のグループ」だけを出す
SELECT
    prefecture,
    COUNT(*) AS user_count
FROM
    users
WHERE
    age >= 20           -- 集計前の絞り込み
GROUP BY
    prefecture
HAVING
    user_count >= 10;   -- 集計後の絞り込み

4. 処理の順番を知る:WHEREとHAVINGの決定的な違い

SQLには「記述する順番」と「内部で実行される順番」があります。これを理解すると、WHEREとHAVINGの使い分けで迷うことはなくなります。

4-1. SQLの内部実行順序(標準的な流れ)

  1. FROM / JOIN: どのテーブルからデータを持ってくるか決める。
  2. WHERE: 行そのものをフィルタリングして絞り込む。(集計前)
  3. GROUP BY: 絞り込まれた行をグループに分ける。
  4. 集計関数実行: 各グループで計算(COUNT, SUM等)を行う。
  5. HAVING: 計算結果に基づいてグループをフィルタリングする。(集計後)
  6. SELECT: 表示するカラムを決定する。
  7. ORDER BY: 最終的な結果を並び替える。
  8. LIMIT: 指定した件数だけ出力する。

「WHEREは集計する前のフィルター、HAVINGは集計した後のフィルター」と覚えましょう。WHERE句で集計関数(例: WHERE COUNT(*) > 5)を使うことはできません。なぜなら、WHEREが実行される時点ではまだ「集計」が行われていないからです。


5. 高度な集計:複数グループ化と重複排除(DISTINCT)

中級者へのステップアップとして、もう少し複雑な集計テクニックを見ていきましょう。

5-1. 複数のカラムでグループ化する

「都道府県ごと、かつ性別ごと」のように、複数の基準でグループ分けすることも可能です。

SELECT
    prefecture,
    gender,
    COUNT(*)
FROM
    users
GROUP BY
    prefecture, gender;

5-2. 集計関数内でDISTINCTを使う

「そのカテゴリの商品を購入した『ユニークなユーザー数』を知りたい」という場合、同じ人が何度も購入していても「1人」と数えたいことがあります。ここで DISTINCT を組み合わせます。

-- 商品カテゴリごとに、購入した「のべ人数」ではなく「実人数」を数える
SELECT
    category_id,
    COUNT(DISTINCT user_id)
FROM
    orders
GROUP BY
    category_id;

6. AIを活用した「ビジネス分析レポート」用クエリの生成

「先月の売上を週ごとに集計し、かつ売上の高い順に並べてほしい」
こうした実務的な要望(日本語)をSQLに変換するとき、AIは最高のパートナーになります。特に「日付の処理(WEEKやMONTH)」が絡む集計は、AIが得意とする分野です。

6-1. 分析レポート用プロンプトの例

「あなたはデータサイエンティストです。MySQLで以下のビジネスレポート用クエリを作成してください。

【状況】
ECサイトの『orders(注文)』テーブルと『products(商品)』テーブルがあります。
ordersには『product_id』『amount(数量)』『created_at(注文日)』があります。
productsには『id』『category(カテゴリ名)』『price(単価)』があります。

【要件】
1. 2026年の『月ごと』の売上合計を算出すること。
2. 売上合計は『単価 × 数量』で計算すること。
3. カテゴリごとの内訳も同時に見たい。
4. 売上の高い順に並べ、上位10件を表示すること。
5. SQLの実行順序に基づいて、なぜその場所でGROUP BYを使っているのか初心者向けに解説してください。」

AIは DATE_FORMAT(o.created_at, '%Y-%m') のような複雑な日付関数を駆使し、かつJOINとGROUP BYを完璧に組み合わせたクエリを生成してくれます。さらに、「月とカテゴリの両方をGROUP BYに含める必要があります」といった重要なアドバイスも添えてくれます。


7. 実践:売上分析クエリを組み立てよう

これまでの知識を総動員して、実戦に近いクエリを組み立ててみましょう。

-- カテゴリ別の「売上合計」「平均価格」「商品数」を算出し、
-- 売上が10万円を超えているカテゴリだけを、売上の高い順に表示する
SELECT
    p.category,
    SUM(p.price * o.quantity) AS total_sales,
    AVG(p.price) AS average_price,
    COUNT(o.id) AS order_count
FROM
    products AS p
INNER JOIN
    orders AS o ON p.id = o.product_id
GROUP BY
    p.category
HAVING
    total_sales > 100000
ORDER BY
    total_sales DESC;

このクエリがスラスラと読み書きできるようになれば、あなたはデータベース初心者から完全に脱却したと言えます。


まとめ:集計を制する者がビジネスの意思決定を支える

第6回の講座、お疲れ様でした!

今日は、個別のデータから「全体像」を捉えるための、集計とグループ化について学びました。集計関数、GROUP BY、HAVING、そしてSQLの実行順序。これらは、アプリケーションのダッシュボード画面や、マーケティング資料作成において欠かすことのできない最重要スキルです。

データベースを「ただの保存場所」として使うのではなく、そこから「意味を抽出する」というエンジニアの醍醐味を感じていただけたでしょうか。

次回、第7回「インデックスとパフォーマンス:重いクエリを劇的に速くする方法」では、ついに「速度」の壁に挑みます。データが100万件になっても1秒以内に結果を出すための魔法、インデックスの仕組みを徹底解説します。お楽しみに!

▼ 大規模データの集計を体験しよう ▼

複雑な集計クエリも高速処理
「AlmaLinux 9対応 高性能VPS」

おすすめVPSを詳しく見る

データを分析・活用できる
「希少価値の高いエンジニアへ」

エンジニア転職の無料相談

コメント