応用情報技術者 2010年 春期 午後 問06
販売管理システムで使用する SQL 文の検証に関する次の記述を読んで、設問1~3に答えよ。
X社は、輸入インテリアの販売を行っており、全国に店舗を展開している。現在、店舗ごとの受注・出荷を販売管理システムで管理している。新たに、インターネットで販売を行う Webショップを開設することになり、それに合わせて、Webショップでの受注を管理できる販売管理システムを構築することとした。
新システムの開発に当たって、システム部の Y君が初めてデータベースの設計・開発を任され、Z係長が指導をすることになった。
〔新システムの機能概要〕
・Webショップで受注した商品は、顧客の住所を基に在庫のある最寄り店舗から出荷する。
・受注情報には受注店舗の情報をもち、受注明細には出荷店舗の情報をもつ。
・実店舗での販売においては、受注情報と受注明細の店舗は、販売した実店舗とする。
・Webショップでの販売においては、受注情報の店舗は Webショップとし、受注明細の店舗は出荷を行う最寄り店舗とする。
・Webショップも含めた全店舗の売上を、月次で店舗ごとに集計する。
・Webショップで販売した商品の売上については、出荷した実店舗に配分する。したがって、Webショップの売上を、月次で受注明細の出荷店舗ごとに集計する。
・Webショップの店舗番号は “A09999” とする。
〔新システムで使用する SQL 文の検証〕
(1) テーブル設計
Y君は、新システムのデータベースのテーブルを設計した。設計したテーブルの一部を図1に示す。

(2)SQL文の作成
Y君は、店舗ごとの売上を月次で集計する図2のSQL文と、Webショップの売上を出荷店舗ごとに月次で集計する図3のSQL文を作成した。「:指定月開始日」、「:指定月終了日」は、それぞれ集計対象月の開始日、終了日を表す埋込み変数である。


(3)テストデータの作成
Y君は、SQL文の検証のためにテストデータを作成した。作成したテストデータのうち、店舗、受注、受注明細の各テーブルのデータを表1〜3に示す。



(4) SQL 文の実行
表1~3のテストデータを用いて、指定月開始日を2010年3月1日、指定月終了日を2010年3月31日として図2のSQL文を実行した結果、図4の出力が得られた。

Z係長は図4の出力された結果を見て、このSQL文では、集計の対象となる期間にd店舗の場合は、店舗ごとの売上の集計に出力されないことを指摘した。Y君がSQL文を図5のように修正して実行したところ、期待された結果が得られた。

設問1:図3のSQL文について、(1)、(2)に答えよ。
(1)図3中のa~cに入れる適切な字句を答えよ(bとcは順不同)。
模範解答
a:'A09999'
b:j.受注番号
c:m.受注番号
解説
解答の論理構成
-
Webショップだけを対象にする条件
- 仕様には「Webショップの店舗番号は “A09999” とする。」と明記されています。
- したがって 受注 表の列である 受注店舗番号 が “A09999” である行だけを抽出すれば、Webショップで受け付けた受注だけが対象になります。
- ここが a に入る定数であり、正しくは 'A09999' です。
-
受注表と受注明細表を結び付ける条件
- 集計対象の金額は 受注明細 表に格納されていますが、どの明細が Webショップで受け付けた受注に属するかを判定するために、両表を結合する必要があります。
- 両表を一意に対応付けるキーは受注番号です。
- よって 受注.受注番号 = 受注明細.受注番号 という等価結合が必要になります。
- ここが b と c に入る句であり、順序は問われません。具体的には j.受注番号 と m.受注番号 です。
-
結果
- 以上より
• a:'A09999'
• b:j.受注番号
• c:m.受注番号
が解答となります。
- 以上より
誤りやすいポイント
- Webショップの売上を「受注明細の出荷店舗番号='A09999'」と誤解しやすい
(実際には出荷店舗は実店舗なので別番号)。 - 結合条件を j.受注店舗番号 = m.出荷店舗番号 としてしまうミス
(これでは Webショップ以外の受注まで拾ってしまう)。 - 文字列リテラル 'A09999' をクォートせずに書き、実行時エラーになる。
FAQ
Q: なぜ出荷店舗番号ではなく受注店舗番号で絞り込むのですか?
A: Webショップで受け付けた受注かどうかは「受注店舗番号」で判定すると仕様にあるためです。出荷店舗は配送拠点であり、必ずしも “A09999” ではありません。
A: Webショップで受け付けた受注かどうかは「受注店舗番号」で判定すると仕様にあるためです。出荷店舗は配送拠点であり、必ずしも “A09999” ではありません。
Q: 結合キーに受注日付を使ってはいけませんか?
A: 受注日付だけでは一意に行を識別できず、重複や誤結合が発生するため適切ではありません。主キーである受注番号同士を結ぶのが正解です。
A: 受注日付だけでは一意に行を識別できず、重複や誤結合が発生するため適切ではありません。主キーである受注番号同士を結ぶのが正解です。
Q: 'A09999' をダブルクォートで囲んでも良いですか?
A: 多くの SQL 実装では文字列リテラルはシングルクォートで表記する規約になっています。ダブルクォートは識別子引用符として扱われることがあるため、避けるのが無難です。
A: 多くの SQL 実装では文字列リテラルはシングルクォートで表記する規約になっています。ダブルクォートは識別子引用符として扱われることがあるため、避けるのが無難です。
関連キーワード: INNER JOIN, WHERE句, グループ化, 集計関数, リテラル
設問1:図3のSQL文について、(1)、(2)に答えよ。
(2)図3のSQL文を、指定開始日を2010年3月1日、指定終了日を2010年3月31日として表1~3のテストデータを用いて実行した結果のデータ項目名とデータの値を答えよ。
模範解答

解説
解答の論理構成
-
集計対象となる注文の抽出
- 【問題文】に「Webショップの店舗番号は “A09999” とする。」とあるため、受注テーブル(表2)から 受注店舗番号 = "A09999" のレコードを抜き出します。
- 指定期間は【小問説明】にある「2010年3月1日 〜 2010年3月31日」。この期間に該当し、かつ 受注店舗番号 = "A09999" の受注番号は “50005”, “50006”, “50008” の 3 件です。
-
受注明細との結合
- 受注明細テーブル(表3)を上記 3 つの受注番号で絞り込みます。該当明細は次の 3 行です。
• 受注番号 “50005” : 出荷店舗番号 “A01002” / 受注金額 113000
• 受注番号 “50006” : 出荷店舗番号 “A01001” / 受注金額 36700
• 受注番号 “50008” : 出荷店舗番号 “A01002” / 受注金額 120000
- 受注明細テーブル(表3)を上記 3 つの受注番号で絞り込みます。該当明細は次の 3 行です。
-
出荷店舗別の集計
- 出荷店舗番号で GROUP BY し、SUM(受注金額) を算出します。
• “A01001” : 36700
• “A01002” : 113000 + 120000 = 233000
- 出荷店舗番号で GROUP BY し、SUM(受注金額) を算出します。
-
出力項目
- 図3の SQL は SELECT m.出荷店舗番号, SUM(m.受注金額) AS Web売上分 … GROUP BY m.出荷店舗番号 という構造なので、表示される列は
① 出荷店舗番号
② Web 売上分 - 以上から回答は次のとおりです。
- 図3の SQL は SELECT m.出荷店舗番号, SUM(m.受注金額) AS Web売上分 … GROUP BY m.出荷店舗番号 という構造なので、表示される列は
誤りやすいポイント
- 「受注店舗番号」と「出荷店舗番号」を取り違える
‑ Web集計では “A09999” で受注したレコードだけを対象にし、出荷店舗で GROUP BY する点に注意が必要です。 - 受注テーブル側の日付条件を忘れる
‑ 指定期間に該当しない受注番号を含めると金額が過大になります。 - 明細行を重複して合計してしまう
‑ 受注明細の JOIN 条件は受注番号のみ。意図せずクロス結合になっていないかを必ず確認します。
FAQ
Q: 受注明細に複数商品がある場合、受注金額の列は単価ですか総額ですか?
A: 表3の “受注金額” は明細ごとの金額です。よって同じ受注番号に複数行あれば、その都度加算されます。
A: 表3の “受注金額” は明細ごとの金額です。よって同じ受注番号に複数行あれば、その都度加算されます。
Q: Webショップ自身(店舗番号 “A09999”)の売上は表示されないのですか?
A: 出荷店舗別に配分する仕様のため、Webショップ自身は GROUP BY の対象外です。
A: 出荷店舗別に配分する仕様のため、Webショップ自身は GROUP BY の対象外です。
Q: 期間条件を WHERE ではなく JOIN 内に書くメリットは?
A: 受注テーブルを事前に絞り込んでから結合するため、不要データの結合を防ぎパフォーマンスが向上します。
A: 受注テーブルを事前に絞り込んでから結合するため、不要データの結合を防ぎパフォーマンスが向上します。
関連キーワード: GROUP BY, INNER JOIN, 集計関数, BETWEEN, 外部結合
設問2:
本文中のdに入れる適切な字句を15字以内で答えよ。
模範解答
d:「受注情報が存在しない」
または
「売上がない」
解説
解答の論理構成
- 問題文には、図2の SQL 文について
「SELECT t.店舗番号、t.店舗名、SUM(m.受注金額) AS 金額 FROM (店舗 t INNER JOIN …)」
とあり、2つの INNER JOIN だけで結合していると記載されています。 - 同じく問題文で Z 係長は
「このSQL文では、集計の対象となる期間にd店舗の場合は、店舗ごとの売上の集計に出力されない」
と指摘しています。 - INNER JOIN は結合条件を満たす行しか残りません。したがって、ある店舗が該当月に1件も受注していなければ、サブクエリ p にその店舗番号が現れず、最初の INNER JOIN で除外されてしまいます。
- つまり “d店舗” とは、集計期間中に受注が発生しておらず、売上レコードが存在しない店舗です。
- よって d に入る適切な語句は「売上がない」(または「受注情報が存在しない」)となります。
誤りやすいポイント
- 「出荷がない」「在庫がない」などと読み違え、受注明細の存在有無と混同してしまう。
- GROUP BY で0件を0円として出力できると思い込み、INNER JOIN と LEFT OUTER JOIN の違いを見落とす。
- テストデータでは “A09999” が売上を持っているため、Webショップ用の特別処理と誤解する。
FAQ
Q: なぜ LEFT OUTER JOIN に書き換えると売上0円の店舗も出力できるのですか?
A: 外部結合は結合先が見付からなくても左側の行を残すため、受注が1件も無い店舗も NULL 行として残り、SUM() が 0 で計算されます。
A: 外部結合は結合先が見付からなくても左側の行を残すため、受注が1件も無い店舗も NULL 行として残り、SUM() が 0 で計算されます。
Q: SUM() で NULL を合計するとどうなりますか?
A: SUM() は NULL を無視して合計します。売上が無い店舗は NULL ではなく 0 を出力したいので、COALESCE(SUM(…),0) とする場合もあります。
A: SUM() は NULL を無視して合計します。売上が無い店舗は NULL ではなく 0 を出力したいので、COALESCE(SUM(…),0) とする場合もあります。
Q: 修正後 SQL で FROM 店舗 t e (SELECT … ) p という書き方になった理由は?
A: LEFT OUTER JOIN を明示するために表記順を変え、ON 句を最後に置く ANSI 形式に統一しただけで、論理的には「店舗を左、受注サブクエリを右」にする点がポイントです。
A: LEFT OUTER JOIN を明示するために表記順を変え、ON 句を最後に置く ANSI 形式に統一しただけで、論理的には「店舗を左、受注サブクエリを右」にする点がポイントです。
関連キーワード: 内部結合, 外部結合, 集計関数, NULL, データ欠損
設問3:図5のSQL文について、(1)、(2)に答えよ。
(1)図5中のeに入れる適切な字句を解答群の中から選び、記号で答えよ。
解答群
ア:EXCEPT
イ:INNER JOIN
ウ:INTERSECT
エ:LEFT OUTER JOIN
オ:UNION
模範解答
e:エ
解説
解答の論理構成
- 問題文によると、図4の結果では「集計の対象となる期間にd店舗の場合は、店舗ごとの売上の集計に出力されない」とあります。これは「受注が 0 件」の店舗が結果に現れないことを示しています。
- 図2のSQLでは
店舗 t INNER JOIN (サブクエリ) p ON t.店舗番号 = p.受注店舗番号
という等価結合が行われており、サブクエリ p に該当行が存在しない店舗は結合結果から除外されてしまいます。 - したがって、販売が無かった店舗も必ず左側(店舗表)に残す必要があります。等価結合ではなく、「左の行を必ず残し、右側が無い場合は NULL を補充する」外部結合を指定するのが正解です。
- 外部結合を表すキーワードは LEFT OUTER JOIN です。
- よって、図5中の e に入る字句は「LEFT OUTER JOIN」、解答群では「エ」です。
誤りやすいポイント
- INNER JOIN と LEFT OUTER JOIN の機能差をあいまいに覚えていると、売上ゼロの店舗が欠落する原因に気付けません。
- サブクエリ側を基準にした RIGHT OUTER JOIN でも論理的には解決できますが、設計時点で基表(店舗)を左側に置く方が自然です。
- UNION など集合演算子で補うと考えがちですが、単純な結合条件漏れの問題であり集合演算子は不要です。
FAQ
Q: なぜ FULL OUTER JOIN ではなく LEFT OUTER JOIN なのですか?
A: 必要なのは「店舗に対応する受注が無い場合も店舗を残す」ことだけで、逆に「受注はあるが店舗表に存在しない」ケースは発生しない前提だからです。
A: 必要なのは「店舗に対応する受注が無い場合も店舗を残す」ことだけで、逆に「受注はあるが店舗表に存在しない」ケースは発生しない前提だからです。
Q: 受注テーブルに存在しない店舗番号が紛れ込んだ場合はどうなりますか?
A: LEFT OUTER JOIN では店舗表に基づいて行を生成するため、受注側にだけ存在する店舗番号は結果に現れません。データ整合性は別途チェックが必要です。
A: LEFT OUTER JOIN では店舗表に基づいて行を生成するため、受注側にだけ存在する店舗番号は結果に現れません。データ整合性は別途チェックが必要です。
Q: サブクエリをビューにしても同じ効果がありますか?
A: はい、結合方法(LEFT OUTER JOIN)を維持すれば、サブクエリをビュー化しても結果は同じです。
A: はい、結合方法(LEFT OUTER JOIN)を維持すれば、サブクエリをビュー化しても結果は同じです。
関連キーワード: 外部結合, 等価結合, NULL補完, 集計関数, サブクエリ
設問3:図5のSQL文について、(1)、(2)に答えよ。
(2)図5のSQL文を表1~3のテストデータを用いて実行したときの結果と、図2のSQL文で出力した図4の実行結果と比較し、新たに出力される行をすべて答えよ。なお、解答用紙の行はすべて埋まるとは限らない。
模範解答

解説
解答の論理構成
-
図2のSQLでは
「SELECT t.店舗番号、t.店舗名、SUM(m.受注金額) AS 金額
FROM (店舗 t INNER JOIN (SELECT j.受注店舗番号、j.受注番号 FROM 受注 j
WHERE j.受注日付 BETWEEN :指定月開始日 AND :指定月終了日) p
ON t.店舗番号 = p.受注店舗番号)
INNER JOIN 受注明細 m ON p.受注番号 = m.受注番号」
と “INNER JOIN” を二重に用いています。INNER JOIN は両テーブルに一致行が存在しない場合、行を落としてしまいます。 -
その結果、Z係長が指摘したとおり「集計の対象となる期間にd店舗の場合は、店舗ごとの売上の集計に出力されない」という現象が発生します。具体的には3月中に1件も受注がない店舗が除外されます。
-
テストデータを見ると、表1にある「A01003 渋谷店」は、
・表2「受注テーブル」に “A01003” を持つ行が存在しない
=3月中の受注が0件
となり、図4の結果には出力されていません。 -
図5のSQLでは、FROM 句の最初が
「FROM 店舗 t e (SELECT …) p」
となり、e には “LEFT OUTER JOIN” が入ります。LEFT OUTER JOIN は左表(店舗)をすべて残すため、受注がない店舗も行が生成されます。 -
生成された行は受注明細が無いので SUM(m.受注金額) が NULL になります。
-
以上より、図5を実行すると図4に追加される行は1行だけで、内容は
・店舗番号:A01003
・店舗名:渋谷店
・金額:null
となります。
誤りやすいポイント
- INNER JOIN と LEFT OUTER JOIN の役割を混同し、売上0円の店舗が「0」として出力されると勘違いする。実際には一致行が無いため NULL になる。
- 「受注が無い店舗=売上0円」と「受注はあるが金額が0円」の違いを区別し忘れ、後者まで NULL 行に含めてしまう。
- 取得結果を比較するときに店舗テーブル自体の行数を数え忘れ、追加される行数を過大・過少に見積もる。
FAQ
Q: 金額が null ではなく 0 にしたい場合、どう修正すれば良いですか?
A: SELECT 句で COALESCE(SUM(m.受注金額),0) などの関数を使い、NULL を0に置き換えます。
A: SELECT 句で COALESCE(SUM(m.受注金額),0) などの関数を使い、NULL を0に置き換えます。
Q: RIGHT OUTER JOIN でも同じ結果になりますか?
A: 右側に店舗テーブルを置けば理論上可能ですが、可読性やメンテナンス性を考えると、左側に基準テーブルを置く LEFT OUTER JOIN の方が一般的です。
A: 右側に店舗テーブルを置けば理論上可能ですが、可読性やメンテナンス性を考えると、左側に基準テーブルを置く LEFT OUTER JOIN の方が一般的です。
Q: 集計月を動的に変えるとき、パフォーマンスが落ちます。対策はありますか?
A: 受注日付で絞ったサブクエリにインデックスを張る、ビュー化して統計情報を最新化する、等が有効です。
A: 受注日付で絞ったサブクエリにインデックスを張る、ビュー化して統計情報を最新化する、等が有効です。
関連キーワード: INNER JOIN, LEFT OUTER JOIN, NULL, 集計関数, サブクエリ


