SQL GROUP BYのパフォーマンス改善ガイド!重い処理を高速化する初心者向け対策
生徒
「先生、最近データベースの勉強をしているのですが、データをグループ分けして計算する『GROUP BY』という命令を使うと、なんだか処理に時間がかかる気がするんです。」
先生
「よく気づきましたね!実はGROUP BYは、正しく使わないとコンピュータにとても大きな負担をかけてしまう、ちょっと『重い』命令なんです。」
生徒
「どうして重くなるんですか?ただ仲間外れを探したり、同じ種類でまとめたりするだけですよね?」
先生
「そう思うかもしれませんが、コンピュータの裏側では、膨大なデータの並べ替えや、一時的なメモ作成が頻繁に行われているんですよ。今日は、なぜ遅くなるのか、どうすれば速くなるのかを、初心者の方でもわかるようにじっくり解説しますね。」
1. SQLとは何か?
SQL(エスキューエル)は、データベースと呼ばれる「大量のデータを整理して保存する箱」に対して指示を出すための言語です。例えば、お店の売上リストの中から「先月は何が一番売れたかな?」と探したり、学校の生徒名簿に「新入生を追加」したりするときに使います。
パソコンを触ったことがない方でも、「魔法の注文書」だと思えば簡単です。決められたルールで注文書を書けば、データベースという有能な執事が、瞬時に結果を持ってきてくれます。その注文書の中でも、特定の項目ごとに集計を行うのが今回紹介する「GROUP BY」です。
エンジニアの必須スキル「SQL」を、 図解と豊富な練習問題でゼロから体系的に学びたい人へ。 MySQLやPostgreSQLなど、各種データベースに対応した不朽の入門書です。
SQL 第2版 ゼロからはじめるデータベース操作をAmazonで見る※ Amazon広告リンク
2. GROUP BY(グループバイ)の仕組みを知ろう
「GROUP BY」は、その名の通り「グループごとに分ける」ための命令です。例えば、バラバラに記録された「果物の販売記録」から、「りんご」「みかん」「ぶどう」という種類ごとに売れた個数を合計したいときに使います。
ここで、「テーブル」という用語を覚えましょう。テーブルとは、Excel(エクセル)のような「表」のことです。
テーブル(表)の「1行分」のデータのことを指します。例えば、1回のお買い物の記録が1レコードになります。
まず、以下のような「sales(売上)」テーブルがあるとしましょう。
id | product_name | amount | sale_date
---+--------------+--------+------------
1 | りんご | 100 | 2024-01-01
2 | みかん | 50 | 2024-01-01
3 | りんご | 150 | 2024-01-02
4 | ぶどう | 200 | 2024-01-02
5 | みかん | 80 | 2024-01-03
6 | りんご | 120 | 2024-01-03
この表から「商品名(product_name)ごとに売上の合計(amount)を出したい」場合、以下のようなSQLを書きます。
SELECT product_name, SUM(amount)
FROM sales
GROUP BY product_name;
この命令を実行すると、コンピュータは裏側で以下のような作業をします。
- 「product_name」の列を見て、同じ名前のものを探す。
- 「りんご」は1番、3番、6番だな、と分類する。
- 分類したグループごとに、金額を足し算(SUM)する。
結果は以下のようになります。
product_name | SUM(amount)
-------------+------------
りんご | 370
みかん | 130
ぶどう | 200
3. なぜGROUP BYでパフォーマンス(速度)が落ちるのか?
データが数件なら一瞬ですが、これが数百万件、数千万件になると話が変わります。パフォーマンスが落ちる(動作が重くなる)主な理由は3つあります。
① 並べ替え(ソート)の負担
コンピュータがグループ分けをする際、実は最初にデータをバラバラの状態から「同じ種類が隣り合うように並べ替える」という作業をすることが多いです。図書館のバラバラの本を、著者名ごとに棚に並べ直す作業を想像してください。本が100万冊あったら、並べるだけで日が暮れてしまいますよね。
② 一時テーブルの作成
集計作業中に、コンピュータは「計算用のメモ用紙」をメモリ(一時的な記憶場所)に作ります。データが多すぎると、このメモ用紙が足りなくなり、処理が極端に遅くなります。
③ 全走査(フルスキャン)
「どこに何があるか」という目次がない状態で、1行目から最後の行まで全部チェックすることを「フルスキャン」と言います。1,000ページある辞書を、目次なしで一文字ずつ探していくようなものです。
4. パフォーマンスを劇的に上げる対策:インデックスの活用
一番の解決策は、「インデックス(索引)」を作ることです。インデックスとは、本でいうところの「索引」や「目次」です。
あらかじめ「商品名(product_name)」にインデックスを貼っておくと、データベースはどこに「りんご」があるかを最初から知っているため、並べ替えの手間を大幅にカットできます。
インデックスは魔法ではありません。何でもかんでもインデックスを作ると、今度は「データの追加(保存)」が遅くなるという副作用があります。必要な列にだけ作りましょう。
5. 実践:WHERE句を使ってデータを絞り込む
もう一つの重要なテクニックは、「グループ分けをする前に、計算するデータ自体を減らす」ことです。
例えば、「2024年1月2日以降のデータだけを集計したい」場合、全部をグループ分けしてから日付を見るのではなく、先に日付で切り捨ててからグループ分けをします。
SELECT product_name, SUM(amount)
FROM sales
WHERE sale_date >= '2024-01-02'
GROUP BY product_name;
実行結果(元のテーブルから1月1日のデータを除外して集計):
product_name | SUM(amount)
-------------+------------
りんご | 270
みかん | 80
ぶどう | 200
このように、WHERE(ウェア)を使ってあらかじめ行数を減らすことで、コンピュータの並べ替え作業や計算の負担を大幅に軽減できます。これは、お掃除をする前に「明らかなゴミを捨ててから整理整頓する」のと同じくらい効率的な方法です。
6. よくある間違い:HAVINGとWHEREの違い
初心者がハマりやすいのが、HAVING(ハビング)という命令の使い方です。
- WHERE:グループ分けをする「前」にデータを絞り込む(速い!)
- HAVING:グループ分けをして計算した「後」に結果を絞り込む(遅くなりやすい)
「売上合計が200円以上の商品だけ表示したい」という場合は、計算が終わらないと分からないのでHAVINGを使います。
SELECT product_name, SUM(amount)
FROM sales
GROUP BY product_name
HAVING SUM(amount) >= 200;
product_name | SUM(amount)
-------------+------------
りんご | 370
ぶどう | 200
可能な限りWHEREでデータを削り、どうしても計算後の数値で絞り込みたい時だけHAVINGを使う。これがプロが意識している「高速化のコツ」です。
7. データベースを「重く」しないための心構え
SQLを書くときは、常に「コンピュータに無駄な汗をかかせない」ことを意識しましょう。
データが少ないうちはどんな書き方をしても動きますが、サービスが成長してユーザーが増えると、一通りの不適切なSQLが原因でサイト全体が止まってしまうこともあります。
今回の内容をおさらいすると、
まず第一に、集計対象の列に「インデックス」があるかを確認すること。
第二に、WHEREを使って、少しでも処理する対象の行(レコード)を減らすこと。
第三に、不必要な列までグループ化に含めないことです。
これらを意識するだけで、あなたのSQLは驚くほどスムーズに動くようになります。最初は難しく感じるかもしれませんが、紙に図を書いてデータの流れをイメージしてみるのが、上達への一番の近道ですよ。