応用情報技術者 2016年 秋期 午後 問06
ネットショップの会員管理に関する次の記述を読んで、設問1~4に答えよ。
W社は、日用雑貨の製造・販売事業を国内で展開する中堅企業である。自社直営店やデパートなどでの販売に加えて、一般消費者向けにネットショップでも自社製品を販売している。
ネットショップでは、購入者は会員登録を行う必要がある。会員に対しては、購入の履歴から会員の嗜好を把握してダイレクトメールを発送し、さらに購入金額の合計に応じた会員種別を付与している。
会員種別には一般会員と特別会員があり、特別会員は購入時に5%の割引が適用される。一般会員と特別会員の判定は、月末日のメンテナンス時間(23時30分~23時59分)のバッチ処理(以下、会員種別判定バッチ処理という)によって行われ、当月の購入金額の合計が5万円以上であれば翌月の初めから月末までは特別会員、5万円未満であれば一般会員となる。
W社では、1回の購入金額が少額である日用雑貨の性質から、頻繁に購入する会員(リピータ)を獲得することが重要と考え、リピータが特別会員の資格を維持しやすくなる判定ルールを取り入れた。具体的には、購入の履歴中の1回ごとの購入を購入単位として、その日時の古いものから順に調べて購入金額の合計が5万円に達したら、それより後の日時の購入単位は繰越し扱いとし、翌月以降の会員種別判定バッチ処理の対象に回すことにした。
〔データベースの設計〕
ネットショップの会員管理システム(以下、本システムという)について、E-R図を図1に示す。
購入エンティティの購入ステータス属性は、購入が完了しているか否かを表す。“受注”、“入金済み”、“完了”のいずれかの値をもち、“完了”となったものだけが会員種別判定バッチ処理の対象となる。購入者は、購入単位ごとに代金を支払う。W社は、入金が確認された後に商品を発送し、購入ステータス属性を“完了”とする。
購入エンティティの判定処理状態属性は、“未処理”、“判定処理済み”、“繰越し”のいずれかの値をもつ。
会員エンティティの会員種別属性は、入会時には“一般会員”の値をもち、会員種別判定バッチ処理のたびに、“一般会員”か“特別会員”のいずれかの値が格納される。会員エンティティの会員番号属性には、1以上の整数が格納される。
商品エンティティの商品定価属性には、その商品の定価が格納される。一方、購入明細エンティティの商品単価属性には、会員種別による割引を考慮した販売時の単価が格納される。また、購入エンティティの購入金額属性には、関連する購入明細の商品単価と個数の積を合算した金額が格納される。
なお、本システムでは、E-R図のエンティティ名を表名に、属性名を列名にして、適切なデータ型で表定義した関係データベースによって、データを管理する。

〔会員の嗜好の把握〕
会員の嗜好を把握してダイレクトメールを発送するために、過去1年分の購入の履歴から、各会員がその1年間に購入した商品の商品分類名と商品分類ごとの購入金額合計の一覧(過去の購入済み商品分類一覧)を表示する図2のSQL文を作成した。
なお、“:一年前”は、1年前の日時を表す埋込み変数である。


〔会員種別の判定〕
カーソルを使用した会員種別判定バッチ処理を行う図3のプログラムを作成した。
会員種別判定バッチ処理では、会員の購入の履歴を会員番号と購入日時の昇順に処理を行い、特別会員と判定されるまでの購入の履歴は購入単位ごとに判定処理済みとするが、特別会員と判定された後の購入の履歴は購入単位ごとに繰越しとして、翌月以降の会員種別判定バッチ処理の対象にする。購入の履歴中の購入金額の合計が5万円未満の場合は、全ての購入の履歴を判定処理済みとする。
なお、“:判定対象期限”は判定対象である月の最終日時を表す埋込み変数である。また、変数 kounyu_no、kounyu_kingaku、kaiin_no、goukei、current_kaiin_no、update_flag はそれぞれ適切な型で宣言されているものとする。LOOP から END LOOP までは処理の繰返し範囲を表す。FETCH 文でカーソルから行を取り出して処理を続け、取り出す行がない場合には処理の繰返しを抜ける。

〔会員種別の履歴の確認〕
会員種別について、会員から“自身の会員種別の履歴を確認したい”という要望が多数寄せられた。当該機能を実現するために、図1のE-R図に対して、既存のエンティティとの間に1対多の関連をもつ新しいエンティティを一つ追加し、会員種別の判定後、その結果の適用日時を含めて記録するようにした。
設問1:〔データベースの設計〕について(1)、(2)に答えよ。
(1)図1中のaに入れる適切なエンティティ間の関連を解答群の中から選び、記号で答えよ。
解答群
ア:-
イ:→
ウ:←
エ:↔︎
模範解答
a:イ
解説
解答の論理構成
- 会員と購入の関係を探る
- 【問題文】では「購入者は、購入単位ごとに代金を支払う。」と記載されています。
- ここで “購入者” は “会員” を指し、“購入単位” は “購入” エンティティに相当します。
- 1人の会員が複数回購入できることを確認
- 「会員に対しては、購入の履歴から会員の嗜好を把握して…」という記述から、“履歴” が複数行存在することが読み取れます。
- したがって “会員” 1に対し “購入” は0回以上発生し得る構造です。
- 逆方向を検討
- “購入” 1件が複数の “会員” に属する状況(多対1・多対多)はビジネスルール上成立しません。1回の購入を複数の会員が共有することはないためです。
- 図記号の選択
- 解答群「イ:→」は “1対多” を示す矢印です。
- “会員 ――a――→ 購入” で左辺が1、右辺が多になるため「イ:→」が適切です。
誤りやすいポイント
- 「購入履歴がある会員は必ず特別会員」という誤読
会員種別判定は別途バッチで行われるだけで、関係性には影響しません。 - “多対多” と勘違い
購入明細と商品が多対多なので、同様に考えてしまうケースがありますが、会員と購入は1対多です。 - 矢印の向きを逆にする
「→」の向きが “多” 側に向くことを取り違え、“購入 ―→ 会員” と誤答しやすいです。
FAQ
Q: 会員が購入をキャンセルした場合でも関係は変わりますか?
A: 変わりません。“購入ステータス” 属性で状態を管理するだけで、ER上の関連は1対多のままです。
A: 変わりません。“購入ステータス” 属性で状態を管理するだけで、ER上の関連は1対多のままです。
Q: “会員種別” が “特別会員” になっても関係線は変更しますか?
A: いいえ。会員属性が更新されるだけで、会員と購入のカーディナリティは常に1対多です。
A: いいえ。会員属性が更新されるだけで、会員と購入のカーディナリティは常に1対多です。
Q: “購入明細” を介して多対多になるのでは?
A: “購入明細” は “購入” と “商品” を橋渡しするエンティティです。“会員” は関与していないため、会員―購入の1対多は保たれます。
A: “購入明細” は “購入” と “商品” を橋渡しするエンティティです。“会員” は関与していないため、会員―購入の1対多は保たれます。
関連キーワード: ER図、カーディナリティ、主キー、外部キー、1対多
設問1:〔データベースの設計〕について(1)、(2)に答えよ。
(2)図1中のbに入れる適切な属性名を答えよ。なお、属性名の表記は、図1の凡例に倣うこと。
模範解答
b:商品番号
解説
解答の論理構成
- 図1では「購入明細 ――→ 商品」という 1 対多の関連が設定されています。すなわち「購入明細」側の外部キーは「商品」側の主キーを参照します。
- 【問題文】中で「購入明細エンティティの商品番号」と明示されており、この列には外部キー(破線の下線)が付いています。
- 外部キーの参照先は必ず相手エンティティの主キーです。したがって「商品」エンティティの主キーも同じ名称でなければ整合が取れません。
- 図1の「商品」エンティティの主キー欄 b には下線が必要であり、外部キーと同一名称の「商品番号」を置くことで、1〜3 の要件をすべて満たします。
結論
b に入る属性名は 商品番号 です。
b に入る属性名は 商品番号 です。
誤りやすいポイント
- 「商品名」「商品分類番号」を主キーだと思い込む
→ いずれも重複や変更の可能性があるため主キーには不適切です。 - 外部キーと主キーの名称が一致していなくてもよいと誤解する
→ 名称が一致しないと ER 図の読み取りで整合性を見失いがちです。 - 「商品定価」を主キーに選ぶ
→ 数値属性は同一価格の商品が複数存在し得るため一意性を確保できません。
FAQ
Q: 外部キーと主キーの名前は必ず同一にする必要がありますか?
A: 厳密には DBMS 上で異なっていても参照制約は張れますが、設計書として ER 図を読みやすくするため、同一名称にそろえるのが一般的です。
A: 厳密には DBMS 上で異なっていても参照制約は張れますが、設計書として ER 図を読みやすくするため、同一名称にそろえるのが一般的です。
Q: 主キーを複合キーにする選択肢はありませんか?
A: 「商品」エンティティは単一の行を一意に識別できれば十分なので、管理しやすい単一キー「商品番号」で設計するのが自然です。
A: 「商品」エンティティは単一の行を一意に識別できれば十分なので、管理しやすい単一キー「商品番号」で設計するのが自然です。
Q: 「商品名」に UNIQUE 制約を付ければ主キーにできますか?
A: UNIQUE でも一意性は担保できますが、業務運用で名称変更が生じるリスクがあるため、主キーには避けるべきです。
A: UNIQUE でも一意性は担保できますが、業務運用で名称変更が生じるリスクがあるため、主キーには避けるべきです。
関連キーワード: 主キー、外部キー、一意性制約、ER図、リレーションシップ
設問2:
図2中のc、dに入れる適切な字句又は式を答えよ。なお、表の列名には必ずその表の別名を付けて答えよ。
模範解答
c:SUM(t4.商品単価*t4.個数)
d:t2.購入日時
解説
解答の論理構成
-
集計対象の明細
【問題文】で「購入明細エンティティの商品単価属性には、会員種別による割引を考慮した販売時の単価が格納される。また、購入エンティティの購入金額属性には、関連する購入明細の商品単価と個数の積を合算した金額が格納される。」とあります。
したがって、任意の粒度(今回は商品分類ごと)で購入金額合計を求めるには、購入明細レコードごとに商品単価 × 個数を加算する必要があります。これを SQL で表現すると
sql SUM(t4.商品単価 * t4.個数)となり、別名 t4 を付している点が設問条件を満たします。よって c はSUM(t4.商品単価*t4.個数) -
過去 1 年分という期間条件
商品分類ごとの集計対象を「過去 1 年」に限定する条件を WHERE 句に置く必要があります。対象列は購入日時です。会員ではなく購入の過去分を取り出すために、購入表(別名 t2)の日時列を使います。
sql t2.購入日時 > :一年前よって d はt2.購入日時 -
GROUP BY による集計列の確定
SELECT 句に出力する列(会員番号、氏名、商品分類番号、商品分類名)が GROUP BY 句に列挙されているため、これら以外の列は集計関数で包む必要があります。先ほど導いた式は SUM 集計の典型形であり、GROUP BY 句との整合性も保てます。
誤りやすいポイント
- 商品定価を使ってしまう
割引後価格を表すのは 商品単価 であり、商品定価 を用いると実売上と乖離します。 - SUM(t4.商品単価) のみで済ませる
個数の情報が抜けるため、複数個購入を正しく計算できません。 - WHERE 句で t1.購入日時 と書く
t1 は会員表であり、購入日時を持ちません。表別名を混同するとエラーになります。 - BETWEEN で境界を含め忘れる/> と >= を混同
:一年前 を含めるかどうかで結果が変わるため、運用規約を必ず確認しましょう。
FAQ
Q: HAVING 句で期間条件を付けても良いですか?
A: 機能上は可能ですが、行選択条件は原則 WHERE で行う方が最適化されやすく、可読性も向上します。
A: 機能上は可能ですが、行選択条件は原則 WHERE で行う方が最適化されやすく、可読性も向上します。
Q: INNER JOIN を WHERE 句の等価条件で書いても同じですか?
A: 等価結合であれば動作は同じですが、本問では可読性と最新の SQL 記法を示すために INNER JOIN を用いています。
A: 等価結合であれば動作は同じですが、本問では可読性と最新の SQL 記法を示すために INNER JOIN を用いています。
Q: SUM 関数の式中で掛け算に括弧は必要ですか?
A: 演算子の優先順位上、掛け算は加算より先に評価されるため必須ではありませんが、可読性向上のため (t4.商品単価 * t4.個数) と括弧で囲むことを推奨します。
A: 演算子の優先順位上、掛け算は加算より先に評価されるため必須ではありませんが、可読性向上のため (t4.商品単価 * t4.個数) と括弧で囲むことを推奨します。
関連キーワード: 集約関数、グループ化、外部キー、別名付与、集計条件
設問3:
図3中のe〜hに入れる適切な字句又は式を答えよ。なお、表の列名には必ずその表の別名を付けて答えよ。
模範解答
e:t2.購入ステータス = '完了'
f:ORDER BY t2.会員番号、t2.購入日時
g:goukei >= 50000
h:SET t1.会員種別 = '特別会員'
解説
解答の論理構成
-
会員種別判定バッチ処理の対象を限定
【問題文】では「“完了”となったものだけが会員種別判定バッチ処理の対象」と明言されています。したがってカーソル cur の WHERE 句 e には
sql t2.購入ステータス = '完了'を入れて、“完了”以外は読み込まないようにします。 -
判定順序は会員番号と購入日時の昇順
「会員の購入の履歴を会員番号と購入日時の昇順に処理を行い」とあるため、カーソルの SELECT 末尾 f には
sql ORDER BY t2.会員番号、t2.購入日時を付けて正しい並び順を保証します。 -
特別会員判定のしきい値
ルールは「購入金額の合計が5万円以上であれば … 特別会員」。プログラムでは累計変数 goukei に対して g を評価するので
sql goukei >= 50000が入ります(単位は円、【問題文】の“5万円”をそのまま 50000 と表現)。 -
会員エンティティへの更新内容
特別会員と判定されたら「会員種別属性に“特別会員”を格納」とあるため、更新文 h は
sql SET t1.会員種別 = '特別会員'となります。別名 t1 を忘れず付与します。
誤りやすいポイント
- WHERE 句に “判定処理状態 <> '判定処理済み'” が既に書かれているため、「未処理」や「繰越し」の行も対象に含まれることを見落とし、e に誤って t2.判定処理状態 = '未処理' を入れてしまう。
- 昇順指定を ORDER BY t2.購入日時、t2.会員番号 の順にしてしまい、会員を跨いで購入日時でソートする形になりロジックが崩れる。
- 金額条件を > だけで書き、5万円ちょうどの場合を取りこぼす。
- h で UPDATE 会員 ... 文全体を書いてしまい、設問が求めている「SET 以降」のみの回答形式を外す。
FAQ
Q: ORDER BY に 購入番号 を追加する必要はありませんか?
A: 必要ありません。「会員番号と購入日時の昇順」が要件なので、この2列で一意順序が保証されます。
A: 必要ありません。「会員番号と購入日時の昇順」が要件なので、この2列で一意順序が保証されます。
Q: “判定処理状態” が “繰越し” の行を読み込むのはなぜですか?
A: “繰越し” は前月処理後に残った行であり、当月も判定対象です。'判定処理済み' のみ除外すれば要件を満たします。
A: “繰越し” は前月処理後に残った行であり、当月も判定対象です。'判定処理済み' のみ除外すれば要件を満たします。
Q: 累計変数 goukei をリセットするタイミングは?
A: カーソルで次の会員番号に切り替わった直後(kaiin_no <> current_kaiin_no の分岐)で SET goukei = 0; を行います。これにより会員ごとに独立した合計が計算できます。
A: カーソルで次の会員番号に切り替わった直後(kaiin_no <> current_kaiin_no の分岐)で SET goukei = 0; を行います。これにより会員ごとに独立した合計が計算できます。
関連キーワード: 累積処理、カーソル、外部結合、グループ化、条件分岐
設問4:〔会員種別の履歴の確認〕について、(1)、(2)に答えよ。
(1)追加するエンティティとの間に多対1の関連をもたせる既存のエンティティのエンティティ名を答えよ。
模範解答
会員
解説
解答の論理構成
- 「会員種別について、会員から“自身の会員種別の履歴を確認したい”という要望が多数寄せられた。」と【問題文】にあります。履歴を確認したい主体は“会員”自身です。
- その要望を満たすために「既存のエンティティとの間に1対多の関連をもつ新しいエンティティを一つ追加」すると記述されています。1対多は「1つの既存エンティティ」に対し「多くの履歴レコード」が紐付く関係です。
- 会員種別の履歴は“購入”や“商品”ではなく、あくまでも「会員種別」の変遷を記録するもので、キーとなるのは会員個人です。
- よって「多対1の関連」を受ける既存エンティティ、すなわち“1”側に立つのは「会員」エンティティです。
- したがって解答は「会員」となります。
誤りやすいポイント
- 購入履歴と混同し「購入」を選んでしまう
→ 会員種別は購入単位ではなく会員単位で更新されます。 - 「商品」「商品分類」など他のエンティティを連想する
→ 履歴は商品情報ではなく会員種別情報の時系列データです。 - 1対多と多対1の向きを取り違える
→ 新エンティティ(履歴)が“多”で、既存エンティティ(会員)が“1”です。
FAQ
Q: 履歴用エンティティの主キー設計はどう考えればよいですか?
A: 一般に「会員番号」「適用開始日時」などを複合主キーとし、会員ごと・日時ごとに一意にします。
A: 一般に「会員番号」「適用開始日時」などを複合主キーとし、会員ごと・日時ごとに一意にします。
Q: 既存テーブルに履歴を追加するとパフォーマンスに影響しますか?
A: 履歴は別エンティティとして独立させるため、既存の参照系クエリには影響しにくく、履歴照会時のみ対象となります。インデックス設計で更に負荷を抑制できます。
A: 履歴は別エンティティとして独立させるため、既存の参照系クエリには影響しにくく、履歴照会時のみ対象となります。インデックス設計で更に負荷を抑制できます。
Q: 会員種別変更時のトランザクション管理は必要ですか?
A: 判定バッチ処理で「会員」と「履歴」両方を同一トランザクションで更新し、一貫性を保つ実装が推奨されます。
A: 判定バッチ処理で「会員」と「履歴」両方を同一トランザクションで更新し、一貫性を保つ実装が推奨されます。
関連キーワード: エンティティ追加、1対多関連、履歴管理、データ正規化
設問4:〔会員種別の履歴の確認〕について、(1)、(2)に答えよ。
(2)追加するエンティティに含めるべき属性名を全て答えよ。なお、主キーや外部キーであることを示す下線は付けなくてよい。
模範解答
会員番号、会員種別、適用日時
解説
解答の論理構成
-
要求仕様の抽出
- 【問題文】には「“自身の会員種別の履歴を確認したい”という要望が多数寄せられた」とあります。履歴を残すには判定結果を時系列で蓄積できる構造が不可欠です。
- 同じ段落で「既存のエンティティとの間に1対多の関連をもつ新しいエンティティを一つ追加」とあるため、親側は既存の「会員」、子側が追加エンティティになります。
-
必要なキーの決定
- 1対多の“多”側には親を識別する外部キーが必須です。親は「会員」なので「会員番号」が必要です。
-
履歴として残すべき内容
- 同段落に「会員種別の判定後、その結果の適用日時を含めて記録」と明記されています。
- したがって判定結果そのもの=「会員種別」およびそれが有効になるタイミング=「適用日時」が必要です。
-
以上から、追加エンティティに含めるべき最小構成は
- 「会員番号」
- 「会員種別」
- 「適用日時」
となります。
誤りやすいポイント
- 主キー用に「履歴番号」を付けるかどうかは設計次第ですが、本問は“含めるべき属性”を問うだけなので不要な項目を追加すると失点します。
- 「判定日時」と「適用日時」を混同しがちです。仕様が示すのは「適用日時」です。
- 1対多の多側に外部キーが必須であることを見落とし、「会員種別、適用日時」だけを書いてしまうケースがよくあります。
FAQ
Q: 「判定処理状態」や「購入金額」も一緒に保存した方が良いのでは?
A: 履歴の目的は“いつ・どの会員が・どの会員種別になったか”を確認することです。判定に使った金額や状態は既存テーブルで追跡できるため、本設問の必須属性ではありません。
A: 履歴の目的は“いつ・どの会員が・どの会員種別になったか”を確認することです。判定に使った金額や状態は既存テーブルで追跡できるため、本設問の必須属性ではありません。
Q: 「適用日時」と「判定日時」の両方を持たせる設計もあり得ますか?
A: 設計としては可能ですが、【問題文】は「その結果の適用日時を含めて記録」とだけ述べており、判定日時は要求されていません。
A: 設計としては可能ですが、【問題文】は「その結果の適用日時を含めて記録」とだけ述べており、判定日時は要求されていません。
Q: 履歴テーブルの主キーはどう設計するのが一般的ですか?
A: 典型的には「会員番号+適用日時」を複合主キーにするか、サロゲートキー(連番)を追加します。ただし本問は主キーの指定を求めていません。
A: 典型的には「会員番号+適用日時」を複合主キーにするか、サロゲートキー(連番)を追加します。ただし本問は主キーの指定を求めていません。
関連キーワード: E-R図、履歴管理、1対多関連、外部キー、バッチ処理


