FE Study
FE Study メニュー

学習記録はこのブラウザー内に保存されます。

ホーム手順で学ぶ実習
ログイン不要・無料で学習

WORKED CASE・約25分

売上と入金を結合したら金額が倍に? 集計する粒度からSQLを直す

受注明細と入金明細を同時に結合したときの重複を、小さなデータで再現。先に受注単位へ集約して、顧客別売上・入金・未収を正しく求めます。

公開・確認: / 作成:FE Study編集部

この実習でできるようになること

  • 結合後の一行が何を表すかを説明し、1対多と1対多の掛け合わせを発見できる。
  • 集計する粒度をそろえてから結合する方法を使える。
  • NULL、同額の異なる取引、明細のない受注を含めて検算できる。

前提と課題

架空の文具店で、顧客別の売上と入金を同じレポートへ出します。受注には複数の商品明細があり、入金も分割されることがあります。最初のSQLでは、受注、商品明細、入金明細を一度に結合してからSUMを取りました。

ところが、受注101の売上が実際の2,500円ではなく5,000円になりました。売上も入金も同じように倍になっているため、差額が0円という見た目だけでは異常に気付けません。この演習は税、送料、返金を扱わず、単価は受注明細に記録した取引時点の値とします。

顧客
C1、C2、C3の3件。顧客IDは一意。
受注
101→C1、102→C1、103→C2、104→C3。受注IDは一意。
商品明細(単価×数量)
101:1,000×2と500×1、102:1,200×1、103:800×3、104:なし
入金明細(金額)
101:1,500と1,000、102:1,200、103:1,000、104:なし

自分で求めてみよう

顧客を全て表示し、顧客別の売上、入金、未収額(売上−入金)を求めてください。受注104のように明細がない場合は金額を0円とし、集計の前後で総額が保存されることも確かめましょう。

途中の式や表も紙に書いてから、次の手順と照合してください。

途中の手順と検算

1. まずSQLを使わず、期待する答えを手で作る

受注101の売上は1,000×2+500×1=2,500円、入金は1,500+1,000=2,500円です。102の売上と入金はともに1,200円、103の売上は2,400円、入金は1,000円です。104はどちらも明細がないので、この教材の規則では0円とします。

C1は101と102を持つので売上3,700円、入金3,700円、未収0円です。C2は2,400円、1,000円、1,400円、C3は全て0円になります。この小さな正解表を持っておくと、複雑なSQLの結果がもっともらしい数字でも誤りを発見できます。実際の業務では、明細のない受注を異常として別途通知する設計も考えられます。

1. まずSQLを使わず、期待する答えを手で作るの確認表
顧客売上入金未収
C13,700円3,700円0円
C22,400円1,000円1,400円
C30円0円0円

2. 二つの「多」を同時に結合すると何行になるかを見る

受注101に商品明細が2件、入金明細が2件あるため、両方を受注IDで結合すると2×2=4行になります。一行は商品明細そのものではなく、「ある商品明細とある入金明細の組合せ」です。単価×数量の2,000円は入金2件に対応して2回、500円も2回現れます。

結果として売上は2,000+2,000+500+500=5,000円、入金も1,500+1,000+1,500+1,000=5,000円になります。GROUP BYを顧客IDにしても、重複した入力行の合計を計算する点は変わりません。行が増えた後で集計するだけでは、取引の単位へ戻れないのが原因です。

2. 二つの「多」を同時に結合すると何行になるかを見るの確認表
受注101の組合せ商品明細金額入金明細金額
商品明細1×入金12,0001,500
商品明細1×入金22,0001,000
商品明細2×入金15001,500
商品明細2×入金25001,000

3. 商品明細と入金明細を、それぞれ受注単位へ集約する

二つの明細表を直接つなぐ前に、商品明細を受注IDごとにSUM(unit_price * quantity)、入金明細を受注IDごとにSUM(amount)で集約します。こうして各表を「受注IDごとに最大一行」にすると、一つの受注について金額同士が掛け合わされなくなります。

SQLの共通表式を使うなら、集約部分は次のように表せます。列名・表名はこの演習用です。売上は金額の列を集計するのであり、商品明細の件数や入金の件数を掛ける処理はありません。

WITH line_totals AS ( SELECT order_id, SUM(unit_price * quantity) AS sales FROM order_lines GROUP BY order_id ), payment_totals AS ( SELECT order_id, SUM(amount) AS paid FROM payments GROUP BY order_id )

4. 顧客を起点にLEFT JOINし、0円の表示条件を明示する

全顧客を残すため、customersを起点にorders、line_totals、payment_totalsをLEFT JOINします。受注104には対応する集約行がないので、結合結果の金額はNULLになります。SUMの結果がNULLになる場合をCOALESCEで0へ置き換えると、明細のない顧客も指定どおり表示できます。

SELECT c.customer_id, COALESCE(SUM(l.sales), 0) AS sales, COALESCE(SUM(p.paid), 0) AS paid, COALESCE(SUM(l.sales), 0) - COALESCE(SUM(p.paid), 0) AS unpaid FROM customers c LEFT JOIN orders o ON o.customer_id = c.customer_id LEFT JOIN line_totals l ON l.order_id = o.order_id LEFT JOIN payment_totals p ON p.order_id = o.order_id GROUP BY c.customer_id ORDER BY c.customer_id;

このSELECTは、前の手順のWITH句に続けて実行します。結合キーの一意性も前提です。例えば顧客表に同じIDを重複登録できるなら、集約を直しても再び行が増えます。主キー・外部キーの設計とデータの状態を確認することも、SQLの正しさを支えます。

5. 全体の総額と、誤った修正が通らないデータで検算する

元の商品明細から求めた総売上は2,500+1,200+2,400=6,100円、元の入金総額は2,500+1,200+1,000=4,700円です。レポートの列合計も同じで、未収合計は1,400円になります。顧客が3行表示されることと合わせて確認すると、金額と行の欠落を別々に検査できます。

SUM(DISTINCT 金額)で重複を消す修正は不適切です。1,000円の正当な入金が別々に2件あれば、同額でも2,000円と数える必要があります。重複を判断する単位は金額の値ではなく取引です。元表の合計との照合、同額の別取引、明細0件、明細が2件以上というテストを組み合わせましょう。

解答と、ここまでの要点

商品明細と入金明細を別々に受注IDで集約してから結合します。顧客別の結果はC1=売上3,700円・入金3,700円、C2=2,400円・1,000円、C3=0円・0円です。

売上合計6,100円、入金合計4,700円、未収合計1,400円が元表からの計算と一致します。結合後の行が表す単位を確認することが、重複の根本的な対処になります。

つまずいたときに確認すること

  • 売上と入金の差が合っていることだけで、双方の総額まで正しいと判断する。
  • 1対多の表を二つつないだ後、GROUP BYさえ書けば重複を防げると思う。
  • SUM(DISTINCT 金額)で、同額の正当な取引まで一つにまとめてしまう。
  • LEFT JOINした表の条件を不用意にWHEREへ置き、対応行のない顧客を落とす。

条件が変わっても説明できますか?

選択後に結果と理由を確かめられます。解答は折りたたみからいつでも読めます。

一つの受注に商品明細3件、入金明細2件があります。両明細を受注IDだけで内部結合すると何行ですか。

選んでから確認できます。確認問題は通常の演習記録には加算しません。

解答と理由を見る

解答:6行

商品明細の各1件が入金2件と組になるため3×2=6行です。集計前の行数と一行の意味を考えると、金額が何回現れるかを予測できます。

正当な別取引として1,000円の入金が2件あります。SUM(DISTINCT amount)で得られる値と問題点はどれですか。

選んでから確認できます。確認問題は通常の演習記録には加算しません。

解答と理由を見る

解答:1,000円になり、正当な別取引を失う。

DISTINCTは取引IDではなく金額の値を重複排除します。二つとも1,000なので一つ分しか残りません。受注単位で先に集約してから結合する方法なら、別取引を保ったまま重複結合を避けられます。

次の学習

手順を見ずにもう一度解き、以下の関連問題でも同じ考え方を使えるか確かめましょう。

参照資料とケースの範囲

計算条件、表、シナリオ、確認問題は本サイト独自のものです。用語と考え方の確認には以下の一次資料を使っています。実務では組織の手順や実際の製品仕様も確認してください。

教材の根拠・編集方針 / 誤り・改善点を連絡する