基本情報技術者 2017年 秋期 午後 問03
会員制通信販売事業者における会員販売データ管理に関する次の記述を読んで、設問1〜4に答えよ。
清涼飲料水の会員制通信販売事業を運営するD社では、販売促進と商品管理の効率化を目的に会員情報や販売情報を管理するシステム(以下、販売管理システムという)を、事業開始当初から導入している。注文の受付は電話応対で行い、電話の受付時間は8時から20時までである。
販売管理システムで利用するデータベースの表構成とデータ格納例を、図1に示す。下線付きの項目は主キーである。
設問1:
問題文を見る 販売促進のために、コーヒーの新商品案内のはがきを送ることになった。その際、購入しそうな会員に効率よく案内するために、2016年の1月1日から12月31日までの1年間において、分類がコーヒーである商品を5回以上購入し、かつ、その購入額の合計が10,000円以上である会員の氏名、郵便番号、住所を抽出することにした。ここで、1回の購入は販売明細表の1行に該当するものとする。次のSQL文のに入れる正しい答えを、解答群の中から選べ。
SELECT 会員表.氏名, 会員表.郵便番号, 会員表.住所
FROM 会員表 WHERE 会員表.会員番号 IN
(SELECT 販売表.会員番号
FROM 販売表, 販売明細表, 商品表
WHERE 販売表.伝票番号 = 販売明細表.伝票番号 AND
商品表.商品番号 = 販売明細表.商品番号 a)
aに関する解答群
ア:AND 販売表.販売日 >= '2016-01-01' AND 販売表.販売日 <= '2016-12-31'
AND 商品表.分類 = 'コーヒー'
AND 商品表.単価 * 販売明細表.個数 >= 10000
GROUP BY 販売表.会員番号
HAVING COUNT(*) >= 5
イ:AND 販売表.販売日 >= '2016-01-01' AND 販売表.販売日 <= '2016-12-31'
AND 商品表.分類 = 'コーヒー'
GROUP BY 販売表.会員番号
HAVING SUM(商品表.単価 * 販売明細表.個数) >= 10000
AND COUNT(*) >= 5
ウ:AND 販売表.販売日 >= '2016-01-01' AND 販売表.販売日 <= '2016-12-31'
GROUP BY 販売表.会員番号
HAVING 商品表.分類 = 'コーヒー'
AND SUM(商品表.単価 * 販売明細表.個数) >= 10000
AND COUNT(*) >= 5
エ:GROUP BY 販売表.会員番号
HAVING 販売表.販売日 >= '2016-01-01' AND 販売表.販売日 <= '2016-12-31'
AND 商品表.分類 = 'コーヒー'
AND SUM(販売表.販売額) >= 10000
AND COUNT(*) >= 5
模範解答
a:イ
解説
解答の導き方
求めるのは、2016年の1月1日から12月31日までに、コーヒーの商品を5回以上購入し、その購入額の合計が10,000円以上となる会員です。
まず、どの表から条件に必要な情報を得るかを整理します。販売表には販売日と会員番号があり、販売明細表には伝票番号、商品番号、個数があります。商品表には分類と単価があります。問題文のSQLでは、伝票番号と商品番号を使って3つの表の情報を対応付けています。表どうしを共通の値で対応付ける処理を、結合といいます。
次に、条件を「購入ごとの条件」と「会員ごとの条件」に分けます。
- 「2016年の1月1日から12月31日まで」と「分類がコーヒー」は、集計に含める購入を絞る条件です。販売日の条件は販売表から、分類の条件は商品表から判定します。
- 「購入額の合計が10,000円以上」と「5回以上」は、絞り込んだ購入を会員ごとに集計してから判定する条件です。
購入ごとの条件は、集計前にWHERE句で指定します。WHERE句は、集計対象にする行を先に選ぶための指定です。そのため、販売日と分類の条件はGROUP BY句より前に置きます。
次に、会員ごとに購入をまとめます。GROUP BY句は、指定した項目ごとに行をまとめる指定です。ここでは会員番号ごとに集計するので、GROUP BY 販売表.会員番号とします。
会員ごとの集計結果は、HAVING句で判定します。HAVING句は、集計した後のグループを選ぶための指定です。購入額は、各明細の「単価×個数」を合計します。つまり、SUM(商品表.単価 * 販売明細表.個数)が10,000円以上かを調べます。また、問題文には「1回の購入は販売明細表の1行に該当する」とあるため、COUNT()で明細の行数を数え、5以上かを調べます。SUMは合計を、COUNT()は行数を数える集計関数です。
このように、WHERE句で期間と分類を絞り、GROUP BY句で会員ごとにまとめ、HAVING句で購入額の合計と行数を判定する条件になっているのはイです。
誤りやすいポイント
- 1行ごとの購入額が10,000円以上かを調べるのではありません。複数の購入額を合計して10,000円以上かを調べます。そのため、単価と個数の積をWHERE句で判定するアは適切ではありません。
- 「分類がコーヒー」は集計する行を選ぶ条件です。集計後にHAVING句で判定するのではなく、WHERE句で指定します。したがって、分類の条件をHAVING句に置くウは適切ではありません。
- 販売表の販売額は、伝票全体の販売額です。コーヒー以外の商品を含む可能性があり、コーヒー商品の購入額の合計とは一致しません。また、集計対象の明細行ごとに伝票の販売額を足すと、同じ伝票の販売額を重複して数えることもあります。そのため、販売額を合計するエは適切ではありません。
- COUNT(*)が数えるのは、WHERE句で選ばれた後の行です。期間外やコーヒー以外の商品を除いたうえで、会員ごとの行数を数えます。
FAQ
Q: なぜ販売表の伝票数ではなく、販売明細表の行数を数えるのですか?
A: 問題文で「1回の購入は販売明細表の1行に該当する」と決められているためです。条件に合う明細の行数をCOUNT(*)で数えます。
A: 問題文で「1回の購入は販売明細表の1行に該当する」と決められているためです。条件に合う明細の行数をCOUNT(*)で数えます。
Q: なぜ購入額を販売表の販売額ではなく、単価と個数から計算するのですか?
A: 対象はコーヒー商品の購入額です。商品表の単価に販売明細表の個数を掛ければ、その明細にある商品の購入額を求められます。それを会員ごとに合計します。
A: 対象はコーヒー商品の購入額です。商品表の単価に販売明細表の個数を掛ければ、その明細にある商品の購入額を求められます。それを会員ごとに合計します。
Q: WHERE句とHAVING句は、どう使い分けますか?
A: WHERE句は集計前に行を選びます。HAVING句は、SUMやCOUNT(*)などで集計した後に、集計結果を使ってグループを選びます。
A: WHERE句は集計前に行を選びます。HAVING句は、SUMやCOUNT(*)などで集計した後に、集計結果を使ってグループを選びます。
関連キーワード: SQL、WHERE句、GROUP BY句、HAVING句、集計関数、結合
設問2:
問題文を見る 商品表の単価を何回でも変更できるようにする。併せて、販売時点の単価が分かるように、販売明細表の項目として販売時点の単価を追加することにした。変更した販売明細表の表構成を、図2に示す。商品表の単価の変更は、当日の受付時間前に行う。販売時点の単価の追加によって得ることができる情報として最も適切な答えを、解答群の中から選べ。

解答群
ア:ある時、ある商品をある会員が購入した単価と、その直後に変更された単価との価格差
イ:実際に購入された商品の、販売時点の単価の変遷
ウ:全ての商品の、単価の変遷
エ:全ての商品の、直近の単価変更日の前日における単価
模範解答
イ
解説
解答の導き方
この設問は、販売明細表に「販売時点の単価」を追加すると、どのような情報を後から確認できるかを問うています。
まず、商品表の単価は変更されます。商品表に現在の単価だけを保存していると、単価を更新したときに、変更前の単価は分からなくなります。
そこで、図2のように販売明細表にも販売時点の単価を記録します。販売明細表には、伝票番号や商品番号、個数などが記録されるため、販売時点の単価を追加すれば、「どの商品が、どの販売時点で、いくらだったか」を販売記録と結び付けて確認できます。
たとえば、ある商品が単価100円のときに売れ、その後、単価が120円に変更されてから再び売れたとします。販売明細表に販売時点の単価が記録されていれば、実際に購入された商品について、販売時点ごとの単価をたどれます。
ただし、単価を変更しても、その商品が購入されなければ、販売明細表にはその単価は記録されません。そのため、購入の有無にかかわらず全商品の単価変更を記録できるわけではありません。
このことから、確認できるのは「実際に購入された商品の、販売時点の単価の変遷」です。これに当たるのはイです。
誤りやすいポイント
- 販売時点の単価を記録することで、全商品の単価変更履歴が残ると考えてしまう点です。販売明細表に記録されるのは、実際に販売された商品です。
- 販売時点の単価が分かれば、次の単価変更との差も必ず分かると考えてしまう点です。単価変更後に商品が売れなければ、その変更後の単価は販売明細表に残りません。
- 商品表の単価と販売明細表の単価を混同する点です。商品表には変更後の単価が保存されますが、販売明細表に追加する単価は、その販売が行われた時点の単価です。
FAQ
Q: 商品表の単価を変更するだけでは、変更前の単価は分からないのですか?
A: 商品表の単価を新しい値で上書きすると、変更前の値は商品表から確認できなくなります。販売時点の単価を販売明細表にも記録しておけば、実際に販売されたときの値は残ります。
A: 商品表の単価を新しい値で上書きすると、変更前の値は商品表から確認できなくなります。販売時点の単価を販売明細表にも記録しておけば、実際に販売されたときの値は残ります。
Q: 販売時点の単価を記録すれば、単価を変更した日時も分かりますか?
A: 販売明細表に記録されるのは、販売時点の単価です。単価を変更した日時そのものを記録する項目がなければ、変更日時までは分かりません。
A: 販売明細表に記録されるのは、販売時点の単価です。単価を変更した日時そのものを記録する項目がなければ、変更日時までは分かりません。
Q: 販売されなかった商品の単価変更も分かりますか?
A: 分かりません。販売明細表に記録されないため、単価変更の履歴をすべて確認するには、別途、単価変更の記録を保存する必要があります。
A: 分かりません。販売明細表に記録されないため、単価変更の履歴をすべて確認するには、別途、単価変更の記録を保存する必要があります。
関連キーワード: 販売明細表、商品表、データベース、販売時点単価、履歴管理
設問3:
問題文を見る 商品表の単価を変更できるようにした後の販売状況を把握するために、2017年の1月1日から6月30日までの半年間を対象に、商品表の分類別の販売額の合計(合計販売額)を会員の年齢ごとに求めて、出力したい。年齢は2017から生年を引いた値とする。次のSQL文のに入れる正しい答えを、解答群の中から選べ。ここで、b1~b3に入れる答えは、bに関する解答群の中から組合せとして正しいものを選ぶものとする。
SELECT 年齢, 分類, b1 AS 合計販売額
FROM
(SELECT 2017 - 会員表.生年 AS 年齢, 商品表.分類, b2
FROM 会員表, 販売表, 販売明細表, 商品表
WHERE 会員表.会員番号 = 販売表.会員番号 AND
販売表.伝票番号 = 販売明細表.伝票番号 AND
販売明細表.商品番号 = 商品表.商品番号 AND
販売表.販売日 >= '2017-01-01' AND
販売表.販売日 <= '2017-06-30'
) FACTTB
GROUP BY b3
ORDER BY 年齢 ASC, 合計販売額 DESC
bに関する解答群


模範解答
b:オ
解説
解答の導き方
求めるのは、2017年の1月1日から6月30日までの販売について、会員の年齢と商品の分類ごとに販売額を合計した値です。どの情報を使い、どう集計するかを順に確認します。
-
商品1種類ごとの販売額を求めます。
図2の販売明細表には、「販売時単価」と「個数」があります。商品表の単価を後から変更できるようになると、商品表にある単価は販売時の単価と異なる可能性があります。そのため、販売したときの実際の金額を求めるには、販売明細表の販売時単価を使います。商品1種類ごとの販売額は、次の計算です。販売時単価 × 個数 -
b2では、集計に必要な情報を内側のSELECT文で取り出します。
内側のSELECT文では、年齢と分類に加えて、販売時単価と個数を取り出す必要があります。年齢は問題文の指定どおり、2017から生年を引いて求めます。したがって、b2には「販売明細表.販売時単価, 販売明細表.個数」が入ります。商品表から現在の単価を取り出すのではなく、販売明細表に記録された販売時単価を使う点が重要です。 -
b1では、販売額を合計します。
内側のSELECT文で取り出した販売時単価と個数を掛けて、商品1種類ごとの販売額を求めます。それを年齢と分類の組合せごとに合計するため、b1には「SUM(販売時単価 * 個数)」が入ります。SUMは、複数の値を足し合わせるための集約関数です。販売表の販売額は伝票全体の金額です。1枚の伝票に複数の分類の商品が含まれる場合、その金額だけでは分類ごとの販売額を分けられません。そのため、販売明細表の商品ごとの販売額を計算して合計します。 -
b3では、年齢と分類でまとめます。
同じ年齢で同じ分類の商品を買った販売明細をまとめて、その販売額を合計します。このように、どの項目の値が同じ行をまとめるか指定するのがGROUP BYです。よって、b3には「年齢, 分類」が入ります。合計販売額は、まとめた後に計算される値なので、GROUP BYの対象には含めません。
以上から、b1は「SUM(販売時単価 * 個数)」、b2は「販売明細表.販売時単価, 販売明細表.個数」、b3は「年齢, 分類」となる組合せを選びます。これは解答群のオです。
誤りやすいポイント
-
商品表.単価を使ってしまう
商品表の単価は変更されることがあります。過去の販売額を正しく求めるには、販売時点の単価を記録した販売明細表.販売時単価を使います。 -
販売表.販売額を分類ごとに合計してしまう
販売表.販売額は伝票全体の金額です。商品分類ごとの販売額を求めるには、商品ごとの販売明細から金額を計算する必要があります。 -
GROUP BYに合計販売額を含めてしまう
合計販売額は集計して求める値です。集計のまとまりを作る項目は年齢と分類なので、GROUP BYには「年齢, 分類」を指定します。
FAQ
Q: 年齢は、販売した日の年齢を計算するのですか?
A: いいえ。設問では年齢を2017から生年を引いた値と指定しています。販売日ごとの年齢ではなく、この計算式を使います。
A: いいえ。設問では年齢を2017から生年を引いた値と指定しています。販売日ごとの年齢ではなく、この計算式を使います。
Q: なぜ個数だけでなく、販売時単価も必要なのですか?
A: 個数だけでは販売額にならないためです。販売時単価に個数を掛けることで、商品ごとの販売額を求められます。
A: 個数だけでは販売額にならないためです。販売時単価に個数を掛けることで、商品ごとの販売額を求められます。
Q: 販売日の条件は、年齢や分類と一緒にGROUP BYへ入れますか?
A: 入れません。販売日は対象期間の販売を絞り込む条件に使います。結果をまとめる基準は、設問で求められている年齢と分類です。
A: 入れません。販売日は対象期間の販売を絞り込む条件に使います。結果をまとめる基準は、設問で求められている年齢と分類です。
関連キーワード: SQL、集約関数、GROUP BY、表結合、販売時単価
設問4:
問題文を見る 入荷情報を管理するシステム(以下、入荷管理システムという)を販売管理システムと同時に運用開始している。入荷管理システムで利用するデータベースの表構成を図3に示す。
ビュー入荷集計表は運用開始から現在までの入荷数の総数を表示する。さらに販売総数を把握するためにビュー販売集計表を、最新の在庫数を把握するためにビュー在庫表を作成する。ビュー在庫表は一度でも入荷した商品は在庫数ゼロでも表示する仕様である。データベースに追加する表の構成を、図4に示す。
次の記述中のに入れる正しい答えを、解答群の中から選べ。ここで、c1とc2に入れる答えは、cに関する解答群の中から組合せとして正しいものを選ぶものとする。
図1~図3の表を用いて、図4のビュー販売集計表を作成するための必要最小限の表の数はc1である。図4のビュー在庫表は、ビュー販売集計表を用いて作成する。このとき、ビュー在庫表を作成するための必要最小限の表の数は、ビュー販売集計表も含めてc2である。


cに関する解答群


模範解答
c:ア
解説
解答の導き方
この設問では、まず「販売総数」を作るためにどの表が必要かを考え、次に「在庫数」を作るためにどの表を組み合わせるかを考えます。ビュー(元の表から必要なデータを取り出して表示する表)も、作成元として利用できます。
-
ビュー販売集計表を作るための表を考えます。図4のビュー販売集計表には、「商品番号」と「販売総数」があります。図1の販売明細表には、商品ごとの「商品番号」と販売した「個数」が記録されています。したがって、商品番号ごとに個数を合計すれば、販売総数を求められます。この集計に必要なのは販売明細表だけです。販売表には伝票ごとの販売日や販売額などはありますが、商品ごとの個数はありません。また、商品表の商品名や単価も、販売個数の集計には使いません。そのため、ビュー販売集計表を作るための必要最小限の表の数、c1は1です。
-
ビュー在庫表を作るための表を考えます。図3のビュー入荷集計表には、商品ごとの入荷総数があります。先ほどのビュー販売集計表には、商品ごとの販売総数があります。在庫数は、入荷総数から販売総数を差し引いて求めます。在庫数 = 入荷総数 − 販売総数ただし、問題文には「一度でも入荷した商品は在庫数ゼロでも表示する」とあります。そこで、入荷集計側の商品を基準にして、販売集計側の情報を商品番号で結び付けます。片方に対応する行がなくても、基準側の行を残す結合を外部結合といいます。ここでは、入荷集計側のすべての商品を残す必要があります。販売集計側に商品がない場合は、その商品の販売総数を0として扱います。この場合、在庫数は入荷総数と同じです。販売記録がないことだけでは、在庫数が0とはいえません。在庫数が0になるのは、販売総数が入荷総数と等しい場合です。ビュー在庫表の作成には、ビュー入荷集計表とビュー販売集計表の2つが必要です。問題文ではc2にビュー販売集計表も含めるので、c2は2です。
誤りやすいポイント
- 販売表だけを使おうとしないようにします。商品ごとの販売個数は、販売明細表にあります。
- 商品表も必須ではありません。商品名などを表示する設問ではありません。
- 販売記録がない商品を、在庫数0と考えないようにします。販売記録がないときの販売総数は0であり、在庫数は入荷総数です。
- 入荷した商品をすべて表示するには、販売集計側だけを基準にしたり、両方に行がある場合だけ残す結合にしたりしてはいけません。入荷集計側の商品を残す必要があります。
FAQ
Q: 販売記録がない商品は、ビュー販売集計表に表示されますか?
A: 販売明細表にその商品の行がなければ、ビュー販売集計表には現れません。ビュー在庫表では入荷集計側の商品を残し、販売総数を0として計算します。
A: 販売明細表にその商品の行がなければ、ビュー販売集計表には現れません。ビュー在庫表では入荷集計側の商品を残し、販売総数を0として計算します。
Q: 販売記録がない商品の在庫数は、なぜ0とは限らないのですか?
A: 販売総数が0でも、入荷総数があればその分は在庫として残るためです。在庫数が0なのは、入荷総数と販売総数が等しい場合です。
A: 販売総数が0でも、入荷総数があればその分は在庫として残るためです。在庫数が0なのは、入荷総数と販売総数が等しい場合です。
Q: ビュー在庫表を作るときに、なぜ入荷集計側を基準にするのですか?
A: 「一度でも入荷した商品は在庫数ゼロでも表示する」という条件を満たすためです。販売集計側に行がない商品も、入荷集計側を基準にすれば表示できます。
A: 「一度でも入荷した商品は在庫数ゼロでも表示する」という条件を満たすためです。販売集計側に行がない商品も、入荷集計側を基準にすれば表示できます。
関連キーワード: ビュー、集計、外部結合、リレーショナルデータベース




