【SQL入門】複数のデータをグループ化するGROUP BYの使い方

こんにちは、宮原です。
 
早速ですが、今回はSQLのGROUP BYについて応用例を交えながらお話をさせていただこうと思います。ある要素の最小値と最小値以外にも要素を取得するため、業務でSQLを書く機会がありました。
 
なんとなくGROUP BYを使えばいいのでは? とは思っていたものの、GROUP BYの役割や使い方がいまいち分かっておらず、SQLが書けず作業が止まってしまう……ということがあり、未経験の方でもしかしたら同じ経験をされたことがあるかもしれないと思い、本記事を書かせていただきました。

説明時の開発環境

今回の説明で使用している開発環境は以下のとおりです。
 

PostgreSQL
15.2

GROUP BYとは

ではGROUP BYとは、どんな役割を持っているのでしょうか。Oracle公式サイトによると、以下のように定義されています。
 

GROUP BY句はSELECT文で使用して、複数行にわたるデータを収集し、結果を1つ以上の列または式でグループ化するもの

GROUP BY句 – Oracle Help Center

 
つまりGROUP BYは、複数のデータをグループ化してくれる役割があります。

GROUP BYの使い方

GROUP BYの使い方は下記のとおりです。
 
SELECT 要素名 FROM テーブル名 GROUP BY グループ化したい要素名;
 
GROUP BYの後ろにグループ化したいカラム名を書くというシンプルな使い方です。実際にGROUP BYを簡単な例題をもとに見ていきましょう。使用するテーブルは下記になります。

2022年12月に貸し出された日付と映画タイトルの本数データを知りたいときは、以下のSQLを書けば簡単に取得できます。貸出日ごとで同じ値になるレコードをひとつにまとめて出力するため、GROUP BY句に貸出日を書きます。また、映画タイトルをカウントしているので「映画タイトル本数」という別名を付けています。

SELECT
  COUNT(映画タイトル) AS 映画タイトル本数
  , 貸出日
FROM
  映画テーブル
GROUP BY
  貸出日;


ちなみに例題のSQLでも使用しているCOUNT関数は集約関数と呼ばれています。集約関数の代表的なものではCOUNT関数、MAX関数、MIN関数、SUM関数、AVG関数などがあります。グループ化したレコードに対して、集約関数を用いて値を取得することができます。

GROUP BYで引っかかりやすいこと

ここまでは、簡単なグループ化を見ていきました。GROUP BYについて、理解してきたところでもう少し複雑な例を確認していきたいと思います。使用テーブルは下記になります。

映画ID・映画タイトル・貸出日の全てをグループ化した項目を表示したい場合、GROUP BY句に映画IDと映画タイトルを書いてあげます。SQLは下記のとおりです。

SELECT
  映画ID
    , 映画タイトル
    , 貸出日
FROM
    映画テーブル 
GROUP BY
    映画ID
    , 映画タイトル
   , 貸出日;

残念ながらこのSQLでは期待している結果は取得できません。理由としては以下の図のように、映画IDと映画タイトルで集約できる項目に対して、貸出日が複数存在し、複数のデータの中でどれを表示するかSQL側で判断ができないためです。

解決策の一つとして、貸出日に集約関数を使用する方法があります。今回は、貸出日をカウントしているので貸出回数の別名を付けています。
SELECT
    映画ID
    , 映画タイトル
    , COUNT(貸出日) AS 貸出回数
FROM
    映画テーブル 
GROUP BY
    映画ID
    , 映画タイトル;

応用例

ここまで、基礎的なGROUP BYについて学んできました。基本を学んだところで、応用した例を見ていきましょう。
 

上記のテーブルを参考に「各月で一番高かった売上・映画タイトル・各月の売上集計日」を取得してみましょう。まとめてデータを取得しようとすると混乱してしまうので、各ブロックに分けて考えてみます。大きく3つのSELECT/FROM/DROUP BYに分けて、各ブロックごとでSQLを作成してみましょう。
 
SELECT
表示したい一番高い売上・映画タイトル・各月の売上集計日を考えていきます。最大値を簡単に求める方法に集約関数のMAX()を使用します。各月の売上集計日の取得方法は、GROUP BY句で後述します。

SELECT MAX(売上合計), 映画タイトル, 各月の売上集計日

FROM
どこのテーブルから要素を取得したいかを考えます。今回は映画売上テーブルから要素を取得します。
FROM 映画売上テーブル

GROUP BY
各月を出すためには、1月/2月/3月……と月ごとでまとめる必要があります。まとめる役割を果たしてくれる構文がありましたよね?そうです。GROUP BYを使用します。
GROUP BY 売上集計日

ただし今テーブルに登録されている売上集計日が’YYYY/MM/DD’になっているため、このままではうまくいきません。’YYYY/MM’に変換(CAST)し、ASで別名を提示しましょう。
GROUP BY to_char(cast(売上集計日 AS date), 'YYYY/MM')

また、映画タイトルも同じタイトルのレコードをひとつにまとめて出力するためにグループ化してあげます。
GROUP BY to_char(cast(売上集計日 AS date), 'YYYY/MM'), 映画タイトル

 
上記で作成した各ブロックのSQLを踏まえれば、「各月で一番高かった売上・映画タイトル・各月の売上集計日」が取得できます。
SELECT
    MAX(売上合計)
    , 映画タイトル
    , to_char(cast(売上集計日 AS date), 'YYYY/MM') AS 各月の売上集計日
FROM
    売上テーブル
GROUP BY
    to_char(cast(売上集計日 AS date), 'YYYY/MM')
    , 映画タイトル;

まとめ

SQLのGROUP BYいかがでしたでしょうか。本記事が同じ経験で迷われた方のお役に立つことができたらと思っております。閲覧いただきありがとうございました!

 
 
 
 
《関連記事》

技術
2023.11.10(Fri)

PostgreSQLで時系列データを分析! 時間帯・曜日別に集計するSQL

#SQL

連番を作るGENERATE_SERIES関数
技術
2022.10.14(Fri)

PostgreSQLで連番を作るGENERATE_SERIES関数を使ってみた

#SQL

INTERSECTとEXCEPT
技術
2022.8.24(Wed)

PostgreSQLで検索結果を結合する! INTERSECTとEXCEPTを使ってみた

#SQL

記事をシェア
MOST VIEWED ARTICLES