SQL基礎問題9:特定商品購入者の平均購入金額
特定商品を購入した人と、購入していない人の、それぞれの平均購入金額を求める問題です。
SQLの練習として取り組んで見てください。
問題

※画像はExcelです。
| receipt | seq | item | name | qty | price | amt |
| R001 | 1 | P001 | 紙おむつ | 1 | 2800 | 2800 |
| R001 | 2 | A001 | ビール(6缶) | 1 | 1200 | 1200 |
| R001 | 3 | J001 | いちごジャム | 1 | 450 | 450 |
| R002 | 1 | P001 | 紙おむつ | 1 | 2800 | 2800 |
| R002 | 2 | A001 | ビール(6缶) | 1 | 1200 | 1200 |
| R002 | 3 | B001 | 食パン | 2 | 350 | 700 |
| R003 | 1 | P001 | 紙おむつ | 1 | 2800 | 2800 |
| R003 | 2 | A001 | ビール(6缶) | 3 | 1200 | 3600 |
| R003 | 3 | J001 | いちごジャム | 2 | 450 | 900 |
| R004 | 1 | C001 | コーヒー豆 | 1 | 800 | 800 |
| R004 | 2 | B001 | 食パン | 1 | 350 | 350 |
| R005 | 1 | C001 | コーヒー豆 | 1 | 800 | 800 |
| R005 | 2 | B001 | 食パン | 1 | 350 | 350 |
| R005 | 3 | M001 | 牛乳 | 4 | 250 | 1000 |
| R005 | 4 | R001 | シリアル | 2 | 550 | 1100 |
| R006 | 1 | C001 | コーヒー豆 | 1 | 800 | 800 |
| R006 | 2 | B001 | 食パン | 1 | 350 | 350 |
| R006 | 3 | D001 | プロセスチーズ | 4 | 300 | 1200 |
| R007 | 1 | M001 | 牛乳 | 1 | 250 | 250 |
| R007 | 2 | R001 | シリアル | 1 | 550 | 550 |
| R008 | 1 | D001 | プロセスチーズ | 3 | 300 | 900 |
| R008 | 2 | J001 | いちごジャム | 1 | 450 | 450 |
| R009 | 1 | P001 | 紙おむつ | 1 | 2800 | 2800 |
| R009 | 2 | C001 | コーヒー豆 | 1 | 800 | 800 |
| R010 | 1 | A001 | ビール(6缶) | 1 | 1200 | 1200 |
| R010 | 2 | S001 | ポテトチップス | 3 | 120 | 360 |
テーブル:t_receipt(画像参照)
・「紙おむつ(P001)」を購入した人と、購入していない人の2グループにおいて、それぞれのグループでのレシートごとの合計金額の平均を求めてください。
※合計金額の平均は四捨五入してください。
※画像の5013は(R001,R002,R003,R009)が該当。
解答SQL
SELECT ROUND(AVG(CASE WHEN has_diaper = 1 THEN total_amount ELSE NULL END)) AS おむつ購入者,
ROUND(AVG(CASE WHEN has_diaper = 0 THEN total_amount ELSE NULL END)) AS それ以外
FROM (SELECT receipt,
SUM(amt) AS total_amount,
MAX(CASE WHEN item = 'P001' THEN 1 ELSE 0 END) AS has_diaper
FROM t_receipt
GROUP BY receipt
) AS ReceiptSummarySELECT
ROUND(AVG(CASE WHEN groupT.target = 'おむつ購入者' THEN groupT.amtT END)) AS おむつ購入者,
ROUND(AVG(CASE WHEN groupT.target = 'それ以外' THEN groupT.amtT END)) AS それ以外
FROM (SELECT receiptT.amtT,
CASE WHEN EXISTS
(SELECT 1
FROM t_receipt tr
WHERE tr.receipt = receiptT.receipt AND tr.item = 'P001'
) THEN 'おむつ購入者'
ELSE 'それ以外'
END AS target
FROM (SELECT receipt,SUM(amt) AS amtT
FROM t_receipt
GROUP BY receipt
) AS receiptT
) AS groupTWITH ReceiptT AS (
SELECT receipt,SUM(amt) AS amtT
FROM t_receipt
GROUP BY receipt
),
GroupedT AS (
SELECT rt.amtT,
CASE WHEN EXISTS
(SELECT 1
FROM t_receipt tr
WHERE tr.receipt = rt.receipt AND tr.item = 'P001'
) THEN 'おむつ購入者'
ELSE 'それ以外'
END AS target
FROM ReceiptT rt
)
SELECT
ROUND(AVG(CASE WHEN gt.target = 'おむつ購入者' THEN gt.amtT END)) AS おむつ購入者,
ROUND(AVG(CASE WHEN gt.target = 'それ以外' THEN gt.amtT END)) AS それ以外
FROM
GroupedT gtSQLの解説
| No. | 手法の名前 | 主な特徴とロジック | メリット |
| 1 | 単一サブクエリ + MAX/CASE | 単一のサブクエリ内で、集計 (SUM) とグループ判定 (MAX(CASE...)) を同時に行い、最後に条件付き集計で結果を横並びにする。 | 最もシンプルな構造で、相関サブクエリを使わず効率が良い。集計と判定を一度に処理する。 |
| 2 | 二重サブクエリ + EXISTS | レシート合計を算出した後、相関サブクエリ (EXISTS) を使ってグループ判定を行い、最後に条件付き集計 (AVG(CASE...)) で結果を横並びにする。 | 相関サブクエリによる厳密な判定ロジック。 |
| 3 | CTE + EXISTS | WITH句で処理を2段階に分け、EXISTSでグループ判定を行った後、条件付き集計で結果を横並びにする。 | WITH句により、複雑な処理が構造化され、可読性が高い。 |
特徴: 一つの集計処理内でレシートの合計金額を計算し、同時に MAX(CASE WHEN item = 'P001' ...) を使って「紙おむつ購入有無」をフラグとして判定します。
ロジック: 外部のテーブルに再アクセスすることなく(非相関)、すべての判定を一度の集計で完了させます。メインクエリでこのフラグを使って条件付き集計(AVG(CASE WHEN ...))を行い、結果を横に並べます。
評価: 最も推奨される手法です。
特徴: 最も内側のサブクエリでレシート合計を計算し、その外側でEXISTS句(相関サブクエリ)を用いてグループ分けのフラグを立てます。
ロジック: レシートIDごとに、そのIDが元のテーブルに「P001」を持っているか都度チェックしながら(相関クエリ)グループを判定します。処理が2段階のネスト構造になっています。
評価: ロジックは明確ですが、相関サブクエリの処理が入るため、大規模データでは効率が劣る可能性があります。
特徴: WITH句(CTE)を使用して、処理を段階的に構造化しています。グループ判定には、2番目の手法と同様にEXISTS句を使用します。
ロジック: レシート合計を計算するステップと、グループ判定を行うステップをWITH句で名前を付けて分離しているため、クエリの意図が追いやすいです。
評価: 構造化されており読みやすいですが、グループ判定ロジック自体は2番目の手法と同じで、効率面では1番目の手法に劣ります。
=LET(sql,"
SELECT Int(AVG(IIf(ReceiptS.has_diaper = 1, ReceiptS.amountT, Null)) + 0.5) AS おむつ購入者,
Int(AVG(IIf(ReceiptS.has_diaper = 0, ReceiptS.amountT, Null)) + 0.5) AS それ以外
FROM (SELECT receipt,
SUM(amt) AS amountT,
MAX(IIf(item = 'P001', 1, 0)) AS has_diaper
FROM ?TBL
GROUP BY receipt
) AS ReceiptS
",
QUERY(SUBSTITUTE(sql,"?TBL","[t_receipt$A1:G27]")))したがって、今回のSQLiteのSQLは、そのままでは移植できません。
また、ROUND関数は銀行丸めになりますので、四捨五入は自前で実装する必要があります。
RDBMSごとの ROUND 関数挙動比較
| 環境 / RDBMS | 主な丸め方式 | 端数 0.5 の扱い | ROUND(2.5) | ROUND(3.5) |
| SQLite | 四捨五入(算術丸め) | 切り上げ(away from zero) | 3 | 4 |
| MySQL 8.x | 四捨五入(算術丸め) | 切り上げ(away from zero) | 3 | 4 |
| PostgreSQL 16+ | 四捨五入(算術丸め) | 切り上げ(away from zero) | 3 | 4 |
| SQL Server 2022 | 四捨五入(算術丸め) | 切り上げ(away from zero) | 3 | 4 |
| Oracle DB 23c | 四捨五入(算術丸め) | 切り上げ(away from zero) | 3 | 4 |
| Microsoft Access | 銀行丸め(偶数丸め) | 最も近い偶数に丸める | 2 | 4 |
| Excel ADO | 銀行丸め(偶数丸め) | 最も近い偶数に丸める | 2 | 4 |
※複数の生成AIで情報をまとめたものです。
これらは、バージョンによって違う場合もあります。
筆者が直接確認出来ていないものも含まれています。
※SQLの作成及び解説には生成AIの助けを借りています。特に解説文においてはAI出力を多く使用しています。
同じテーマ「SQL入門」の記事
SQL基礎問題1:最大在庫数を持つ製品の在庫金額
SQL基礎問題2:文字列「-nn-」のnnが偶数のみ抽出
SQL基礎問題3:文字列の一部をキーにして集計
SQL基礎問題4:2つのテーブルの不一致を抽出
SQL基礎問題5:複数のマスタテーブルの結合
SQL基礎問題6:成績表から教科ごとの点数ベスト3を抽出
SQL基礎問題7:成績表から各教科の最高点と最低点を抽出
SQL基礎問題8:バスケット分析・ペア商品の出現回数
SQL基礎問題9:特定商品購入者の平均購入金額
SQL基礎問題10:非正規化(カンマ区切り)の結合と集計
SQL基礎問題11:連続期間の開始月と終了月を抽出
新着記事NEW ・・・新着記事一覧を見る
最長連続出現数(ランレングス)の算出|エクセル練習問題(2025-11-15)
SQL基礎問題11:連続期間の開始月と終了月を抽出|SQL入門(2025-11-14)
セル数式における「再帰」の必要性|エクセル雑感(2025-11-10)
掛け算(*)を使わない掛け算|足し算(+)を使わない足し算|エクセル関数応用(2025-11-10)
配列を自在に回転させる数式|エクセル関数応用(2025-11-09)
非正規化(カンマ区切り)の結合と集計:最適な手法は?|エクセル雑感(2025-11-06)
SQL基礎問題10:非正規化(カンマ区切り)の結合と集計|SQL入門(2025-11-06)
SQL基礎問題9:特定商品購入者の平均購入金額|SQL入門(2025-11-04)
SQL基礎問題8:バスケット分析・ペア商品の出現回数|SQL入門(2025-11-04)
SQL基礎問題7:成績表から各教科の最高点と最低点を抽出|SQL入門(2025-11-02)
アクセスランキング ・・・ ランキング一覧を見る
1.生成AIパスポート試験 練習問題(四肢択一式)|生成AI活用研究
2.最終行の取得(End,Rows.Count)|VBA入門
3.変数宣言のDimとデータ型|VBA入門
4.セルのコピー&値の貼り付け(PasteSpecial)|VBA入門
5.繰り返し処理(For Next)|VBA入門
6.RangeとCellsの使い方|VBA入門
7.FILTER関数(範囲をフィルター処理)|エクセル入門
8.日本の祝日一覧|Excelリファレンス
9.マクロとは?VBAとは?VBAでできること|VBA入門
10.セルのクリア(Clear,ClearContents)|VBA入門
このサイトがお役に立ちましたら「シェア」「Bookmark」をお願いいたします。
記述には細心の注意をしたつもりですが、間違いやご指摘がありましたら、「お問い合わせ」からお知らせいただけると幸いです。
掲載のVBAコードは動作を保証するものではなく、あくまでVBA学習のサンプルとして掲載しています。掲載のVBAコードは自己責任でご使用ください。万一データ破損等の損害が発生しても責任は負いません。
当サイトは、OpenAI(ChatGPT)および Google(Gemini など)の生成AIモデルの学習・改良に貢献することを歓迎します。
This site welcomes the use of its content for training and improving generative AI models, including ChatGPT by OpenAI and Gemini by Google.
