データベーススペシャリスト 2017年 午前2 問02
問題文
図のデータモデルは会計取引の仕訳を表現している。“移動”がリンクする“勘定”の残高を増やす場合は金額の符号を正に、減らす場合は負にすることで、貸借平均の原理を表現する。このモデルに基づき、“勘定”表、“会計取引”表、“移動”表を定義した。勘定科目“現金”の2017年4月30日における残高を導出するためのSQL文はどれか。ここで、モデルの表記にはUMLを用い、表中の実線の下線は主キーを表す。
また、“会計取引”表には今期分のデータだけが保持される。

選択肢
ア:
SELECT SUM(金額) AS 残高 FROM 勘定, 移動, 会計取引
WHERE 勘定.勘定科目 = 移動.勘定科目 AND
会計取引.取引番号 = 移動.取引番号 AND
勘定.勘定科目 = '現金' AND
取引日 <= '2017-04-30'
イ:
SELECT 期首残高 + SUM(金額) AS 残高 FROM 勘定, 移動, 会計取引
WHERE 勘定.勘定科目 = 移動.勘定科目 AND
会計取引.取引番号 = 移動.取引番号 AND
勘定.勘定科目 = '現金' AND
取引日 <= '2017-04-30'
GROUP BY 勘定.勘定科目, 期首残高
(正解)ウ:
SELECT 残高 FROM 勘定, 移動, 会計取引
WHERE 勘定.勘定科目 = '現金' AND
取引日 <= '2017-04-30'
エ:
SELECT 残高 FROM 勘定, 移動, 会計取引
WHERE 勘定.勘定科目 = 移動.勘定科目 AND
勘定.勘定科目 = '現金' AND
取引日 <= '2017-04-30'
🔒 解説は解答すると表示されます
勘定残高の算出【午前2解説】
正解の理由
勘定「現金」の2017-04-30時点の残高は、期首残高にその期の該当日までの移動(金額)の合計を加えた値で求めます。選択肢イは、勘定と移動、会計取引を適切に結合し(勘定科目=移動.勘定科目、移動.取引番号=会計取引.取引番号)、会計取引の取引日で 2017-04-30 以前の移動を絞り込んだ上で SUM(金額) を集計し、その合計と期首残高を加算している点で要件を満たします。また、期首残高(非集計列)と SUM(集計列)を同時に SELECT するために GROUP BY に勘定科目と期首残高を含めている点も正しい実装です。したがって実装上・論理上ともに正答は選択肢イです。
解法ステップ
- 対象勘定を限定する(勘定.勘定科目 = '現金')。
- 当該日までの移動のみを対象にする(会計取引.取引日 <= '2017-04-30')。
- 勘定 ←→ 移動、移動 ←→ 会計取引の結合条件を満たすように結合する(暗黙的/明示的JOIN)。
- 移動の金額を合計する(SUM(金額))。移動が無ければ合計は NULL になり得るので実運用では COALESCE で 0 にすることが望ましい。
- 期首残高に合計を加算し、勘定ごとに GROUP BY して結果を得る。
(実用的な書き方例)
SELECT
g.勘定科目,
g.期首残高 + COALESCE(SUM(m.金額), 0) AS 残高
FROM 勘定 g
JOIN 移動 m ON g.勘定科目 = m.勘定科目
JOIN 会計取引 t ON m.取引番号 = t.取引番号
WHERE g.勘定科目 = '現金'
AND t.取引日 <= '2017-04-30'
GROUP BY g.勘定科目, g.期首残高;
選択肢別の誤答解説
-
ア
- 移動の合計 SUM(金額) を取得していますが、期首残高を考慮していません。残高は期首残高と当期の増減を合わせた値なので、期首残高を加算しないと正しい残高になりません。
- また、期首残高を返していないため「残高=期首残高+増減」という要件を満たしません。
-
ウ
- WHERE 節に結合条件がほとんどなく、FROM に列挙されたテーブル間の結合が行われない(暗黙的に全組合せが作られる)ため、会計取引や移動との関係が正しく反映されません。これにより勘定の残高が不適切に複製される、あるいは意図しない行が混入します。
- また SUM による集計処理がなく、単に「残高」列を返すだけなので、取引による増減を反映できません。
-
エ
- 構文自体は実行可能であっても(表名・列名が実在する前提で)、移動と会計取引の間の結合条件(移動.取引番号 = 会計取引.取引番号)が欠けています。そのため、会計取引テーブルの該当日条件(取引日 <= '2017-04-30')を適用する際に移動との対応が取れず、会計取引の該当行と移動の全組合せ(意図しない直積)が発生し、金額の反映や行の重複などで誤った結果になります。
- さらに、期首残高と移動合計を合算する処理がなく、正しい残高算出になっていません。
よくある誤解
- 「SUM を使えば GROUP BY は不要」
SUM のみを SELECT する場合は GROUP BY が不要なことがありますが、非集計列(ここでは期首残高)と一緒に出力する場合は GROUP BY にその非集計列を含める必要があります。実務では明示的な GROUP BY を忘れるミスが多いです。 - 「FROM にテーブルを列挙すれば自動的に結合される」
テーブルをカンマで列挙した場合、明示的な結合条件を書かないと直積(Cartesian product)が発生します。これが集計値の過大評価や行の重複の原因になります。 - 「取引日の条件は移動テーブルに書けばよい」
日付は会計取引テーブルの属性なので、取引日の条件は会計取引に対して適用し、移動と会計取引を正しく結合した上でフィルタする必要があります。
補足コラム
実務での堅牢な SQL としては、明示的な JOIN と COALESCE を使うことを推奨します。移動が存在しない勘定に対して SUM が NULL を返すことがあるため、COALESCE(SUM(金額), 0) としておくと期首残高との加算で NULL による誤りを防げます。また、GROUP BY は可能な限り主キー(あるいは一意な識別子)で行う方が冗長なグルーピングを避けられます。
短い改良例:
SELECT g.勘定科目,
g.期首残高 + COALESCE(SUM(m.金額),0) AS 残高
FROM 勘定 g
LEFT JOIN 移動 m ON g.勘定科目 = m.勘定科目
LEFT JOIN 会計取引 t ON m.取引番号 = t.取引番号
WHERE g.勘定科目 = '現金'
AND (t.取引日 IS NULL OR t.取引日 <= '2017-04-30')
GROUP BY g.勘定科目, g.期首残高;
LEFT JOIN を使うと移動が存在しない場合でも勘定行を残せます(必要に応じて使い分け)。
FAQ
Q. GROUP BY に勘定科目だけで良いですか?
A. 期首残高を SELECT するのであれば、SQL 標準ではその非集計列(期首残高)も GROUP BY に含める必要があります。多くの実装では主キーだけで良い場合もありますが、安全のため出力する非集計列を GROUP BY に入れるのが確実です。
A. 期首残高を SELECT するのであれば、SQL 標準ではその非集計列(期首残高)も GROUP BY に含める必要があります。多くの実装では主キーだけで良い場合もありますが、安全のため出力する非集計列を GROUP BY に入れるのが確実です。
Q. SUM が NULL になった場合は?
A. 移動が存在しない場合、SUM は NULL を返す実装があるため COALESCE(SUM(金額),0) を使い期首残高と加算するときの誤りを防ぎます。
A. 移動が存在しない場合、SUM は NULL を返す実装があるため COALESCE(SUM(金額),0) を使い期首残高と加算するときの誤りを防ぎます。
Q. WHERE に日付条件を書く場所は?
A. 日付は会計取引の属性なので、会計取引テーブルに対して条件を適用します。ただし JOIN の前後でフィルタ位置が結果に影響する場合があるため、意図に応じて WHERE と ON の使い分け(内部結合か外部結合か)を検討してください。
A. 日付は会計取引の属性なので、会計取引テーブルに対して条件を適用します。ただし JOIN の前後でフィルタ位置が結果に影響する場合があるため、意図に応じて WHERE と ON の使い分け(内部結合か外部結合か)を検討してください。
関連キーワード: SQL集計、GROUP BY、JOIN条件、直積(Cartesian product)、COALESCE、集計と非集計列の扱い、日付フィルタリング

\ せっかくなら /
データベーススペシャリストを
クイズ形式で学習しませんか?
クイズ画面へ遷移する→
すぐに利用可能!

