Alma Core Pro かんたんSQL講座

第3回:GROUP BY と集計関数(データをグループ化して平均・合計・件数を瞬時に計算する技術)

1. GROUP BY ってなに?(電卓いらずのグループ集計)

これまでは「1頭ずつ、1行ずつの個別データ」を取り出して見ていました。
しかし、競馬分析では**「馬番ごとの平均勝率は?」「競馬場ごとの平均先行力は?」「種牡馬ごとの産駒数は?」**のように、データをまとまり(グループ)にして計算したい場面が数多くあります。

これを一瞬で解決するのが GROUP BY(グループ・バイ)です。Excelでいう「ピボットテーブル」と同じ作業を、SQLが裏で瞬時に計算してくれます。

🏫 たとえば、学校のテスト集計で言うと:

生徒40人全員の点数を1人ずつ並べるのではなく、「1組」「2組」「3組」というクラスごとにまとめて、各クラスの【平均点】や【最高点】を出すようなものです。

💻 データベースへの命令(SQL)に置き換えると:

GROUP BY horse_number(馬番ごとに分けて)、AVG(senko)(それぞれの先行力の平均)を計算して見せて!」

2. 必ず使う基本の集計関数 5選(実行例と返りデータ)

グループ分けしたあと、何を計算させたいかを SELECT の中に書きます。

関数名 やりたい計算 書き方の例 出力されるもの
COUNT( ) 件数を数える COUNT(*) AS 出走頭数 そのグループにあるデータの「総数」
AVG( ) 平均を出す ROUND(AVG(st), 1) AS 平均ST そのグループの「平均値」(ROUNDで小数第1位に丸める)
MAX( ) 一番大きい値を出す MAX(senko) AS 最高先行力 そのグループの中での「最大値(トップ)」
MIN( ) 一番小さい値を出す MIN(finish_time) AS 最速タイム そのグループの中での「最小値(底値・最速)」
SUM( ) 全部足し合わせる SUM(prize_money_1st) AS 総賞金 そのグループの数値を合計した「総額」
便利な相棒:ROUND(ラウンド)関数

AVG(st) で平均を計算すると、58.33333333... のように小数が長く続いてしまいます。
そこで ROUND(AVG(st), 1) と書くと、**「小数第1位(例: 58.3)」**に綺麗に四捨五入してくれます。

3. GROUP BY の基本構文(1行ずつの意味分解)

ストライド指数テーブル(kol_stride)から、競馬場ごとの登録頭数と平均ST指数を算出する基本構文です。

SELECT 
    venue_code AS 場コード,
    COUNT(*) AS 総頭数,
    ROUND(AVG(st), 1) AS 平均ST指数
FROM kol_stride
WHERE race_date LIKE '202608%'
GROUP BY venue_code
ORDER BY venue_code ASC;

1行ずつの意味を分解

書いたコード 店員(データベース)への命令内容
FROM kol_stride 「ストライド指数の本棚を開いて!」
WHERE race_date LIKE '202608%' 「まず、2026年8月のデータだけに絞り込んで!」(事前フィルター)
GROUP BY venue_code 「残ったデータを、**場コード(競馬場)ごとに山分け**して!」
SELECT venue_code, COUNT(*), AVG(st) 「各競馬場の『場コード』『集まった頭数』『ST指数の平均』を並べて見せて!」
ORDER BY venue_code ASC 「場コードの番号が若い順(昇順)に並べて出力して!」
📋 実行結果(返りデータ例)
場コード総頭数平均ST指数
03 (小倉)28854.2
07 (新潟)31255.8
08 (札幌)26456.1

4. 決定的な違い!「WHERE」と「HAVING」の使い分け

初心者が最もつまずきやすいのが、「条件の絞り込みで WHEREHAVING のどちらを使うか」です。
違いはたった1つ、**「計算する前に絞るか(WHERE)」「計算したあとに絞るか(HAVING)」**です。

キーワード 動くタイミング 使い分けのルール
WHERE グループ化して計算する前 WHERE race_date >= '20260101' のように、テーブルに最初から入っている元の行を絞り込むとき。
HAVING グループ化して計算したあと HAVING COUNT(*) >= 20 のように、**計算し終わった集計結果(件数や平均など)**に対して絞り込むとき。

HAVINGを使った具体例

「馬番ごとの平均ST指数を出したいが、出走数が少ない(10頭未満の)馬番は除外したい」という場合のクエリです。

SELECT 
    horse_number AS 馬番,
    COUNT(*) AS 出走回数,
    ROUND(AVG(st), 1) AS 平均ST指数
FROM kol_stride
WHERE race_date LIKE '2026%'
GROUP BY horse_number
HAVING COUNT(*) >= 10
ORDER BY CAST(horse_number AS INTEGER) ASC;
📋 実行結果(返りデータ例:10件以上ある馬番だけが残る)
馬番出走回数平均ST指数
0114254.8
0213855.1
.........
162456.3

5. 応用技:CASE WHEN を使った条件別カウント

「種牡馬ごとに、産駒の【牡馬の数】と【牝馬の数】を横並びで別々に数えたい」という上級分析も、CASE WHEN を組み合わせれば簡単に実現できます。

仕組み:もし〇〇なら 1 として数える

COUNT(CASE WHEN sex_code = '1' THEN 1 END) ➔ 「もし性別コードが 1(牡馬)なら、1頭としてカウントする」という命令になります。

SELECT 
    sire_horse_name AS 種牡馬名,
    COUNT(*) AS 総産駒数,
    COUNT(CASE WHEN sex_code = '1' THEN 1 END) AS 牡馬数,
    COUNT(CASE WHEN sex_code = '2' THEN 1 END) AS 牝馬数
FROM kol_uma
WHERE sire_horse_name IN ('キタサンブラック', 'ロードカナロア', 'エピファネイア')
GROUP BY sire_horse_name;
📋 実行結果(返りデータ例)
種牡馬名総産駒数牡馬数牝馬数
エピファネイア520268252
キタサンブラック310162148
ロードカナロア780410370

6. そのまま使える!お宝抽出実践レシピ集

以下の枠内のSQLをコピーし、Alma Core Pro の入力欄に貼り付けて ▶ SQL実行📊 Excel用コピー (TSV) をお試しください。

レシピ 1 馬番別の平均能力値分析(内枠・外枠の傾向調査)

馬番ごとの出走頭数と、先行力・追走力・瞬発力の平均値を一括集計し、枠ごとの有利不利を分析します。

SELECT 
    horse_number AS 馬番,
    COUNT(*) AS 出走頭数,
    ROUND(AVG(st), 1) AS 平均ST指数,
    ROUND(AVG(senko), 1) AS 平均先行力,
    ROUND(AVG(tsuiso), 1) AS 平均追走力,
    ROUND(AVG(shunpatsu), 1) AS 平均瞬発力
FROM kol_stride
WHERE race_date LIKE '2026%'
GROUP BY horse_number
HAVING COUNT(*) >= 10
ORDER BY CAST(horse_number AS INTEGER) ASC;
馬番別集計結果

馬番ごとの平均能力値が綺麗に集計され、Excelでの枠順バイアス分析が即座に可能になります

レシピ 2 競馬場別の指数分布サマリー(レベル感と最高値の把握)

各開催場ごとの登録馬数と平均先行力・最高ST指数を瞬時に集計します。

SELECT 
    venue_code AS 場コード,
    COUNT(*) AS 総登録頭数,
    ROUND(AVG(st), 1) AS 平均ST指数,
    MAX(st) AS 最高ST指数,
    ROUND(AVG(senko), 1) AS 平均先行力,
    MAX(senko) AS 最高先行力
FROM kol_stride
WHERE race_date LIKE '2026%'
GROUP BY venue_code
ORDER BY venue_code ASC;
競馬場別集計結果

競馬場ごとのメンバーレベルや先行馬の割合の違いが一目で数値化されます

レシピ 3 競走馬マスタ(kol_uma)から主要種牡馬の産駒数ランキング

登録頭数が15頭以上の主要種牡馬を対象に、牡馬・牝馬の頭数バランスと合計登録数をランキング化します。

SELECT 
    sire_horse_name AS 種牡馬名,
    COUNT(*) AS 登録頭数,
    COUNT(CASE WHEN sex_code = '1' THEN 1 END) AS 牡馬数,
    COUNT(CASE WHEN sex_code = '2' THEN 1 END) AS 牝馬数
FROM kol_uma
WHERE sire_horse_name IS NOT NULL AND sire_horse_name != ''
GROUP BY sire_horse_name
HAVING COUNT(*) >= 15
ORDER BY COUNT(*) DESC
LIMIT 30;
種牡馬別集計結果

主要種牡馬の勢力図と性別バランスがExcel上で一発で集計できます

7. GROUP BY で初心者が必ずハマるエラーと対策

よくあるミス 間違ったコード例 原因と解決策
① グループ化していない個別項目を書く SELECT venue_code, horse_number, AVG(st) FROM kol_stride GROUP BY venue_code; GROUP BY venue_code(競馬場ごと)にまとめているのに、horse_number(個別の馬番)を書くと、どの馬番を出せばいいか分からずエラーや意図しないデータになります。
SELECTには「GROUP BYに書いた項目」か「集計関数(AVG等)」だけを書きましょう。
② WHERE に集計関数を書いてしまう WHERE AVG(st) >= 60 WHERE は計算前の行を見る場所なので、平均値(AVG)は書けません。
集計後の条件は HAVING AVG(st) >= 60 に書きましょう。