応用情報技術者 2021年 秋期 午後 問06
企業向け電子書籍サービスの追加設計と実装に関する次の記述を読んで、設問1~4に答えよ。
H社は、個人会員向けに電子書籍の販売及び閲覧サービス(以下、既存サービスという)を提供する中堅企業である。近年、テレワークの普及に伴い、企業での電子書籍の需要が高まってきた。そこで、既存サービスに加え、企業向け電子書籍サービス(以下、新サービスという)を開発することになった。
新サービスの開始に向けて、企業向け書籍購入サイトを新たに作成し、既存サービスで提供している電子書籍リーダを改修する。新サービスの機能概要を表1に、検討したデータベースのE-R図の抜粋を図1に示す。
このデータベースでは、E-R図のエンティティ名を表名にし、属性名を列名にして、適切なデータ型で表定義した関係データベースによって、データを管理する。


〔一括購入機能の社員割当処理の作成〕
表1中の一括購入機能の概要(2)にある、社員が割当依頼した電子書籍を割り当てる処理を考える。検討した処理の流れを表2に示す。ここで、“一括購入ID”は割当依頼された一括購入IDを、“:企業ID”及び“:社員ID”は割当依頼した社員の企業IDと社員IDを格納する埋込み変数である。

表2のレビューを実施したところ、処理の流れやSQL文に問題はないが、①トランザクションの同時実行制御には専有ロックを用いるように、とのアドバイスを受けた。
〔書籍閲覧機能の作成〕
電子書籍リーダに、社員がログインした際、閲覧可能な重複を含まない書籍の一覧を取得するSQL文を図2に示す。ここで、“:企業ID”及び“:社員ID”は、ログインした社員の企業IDと社員IDを格納する埋込み変数である。また、図2のcには、図1のcと同じ字句が入る。

〔書籍閲覧機能の改善〕
書籍閲覧機能のレビューを実施したところ、既存サービスを個人で利用している社員は、電子書籍リーダのログインIDを個人会員IDから企業IDと社員IDに切り替えて利用しなければならず煩雑である、との指摘を受けた。
そこで、電子書籍リーダに個人会員IDを用いてログインした際、社員として閲覧できる書籍も一覧に追加して閲覧できるように、E-R図に新たに②一つエンティティを追加し、電子書籍リーダに③一つ画面を追加した上で書籍閲覧機能に改修を施した。
設問1:
図1中のa〜cに入れる適切なエンティティ間の関連及び属性名を答え、E-R図を完成させよ。
なお、エンティティ間の関連及び属性名の表記は、図1の凡例に倣うこと。
模範解答
a:→
b:割引率
c:書籍ID
解説
解答の論理構成
-
【問題文】には「企業は負担する上限金額を書籍分類ごとに設定する。」(表1 No.2)とあります。これは「書籍分類」ごとに企業が補助内容を登録することを示唆しており、E-R図でも「企業補助」エンティティは「書籍分類」を参照する必要があります。参照元である「書籍分類」から参照先である「企業補助」へ“1対多”の関係を張るのが自然です。図の表記ルールでは、主キー側(1)から参照側(多)へ実線矢印を書くため、aには「→」が入ります。
-
表1 No.3「割引購入」には「それぞれの企業がH社と契約した一定の割引率を適用した価格で購入できる。」と明記されています。したがって、割引率は企業が保持すべき属性です。E-R図で属性不足が指摘されているのは「企業」エンティティのbですから、ここに入る語は「割引率」となります。
-
書籍閲覧機能のSQL(図2)の上段は
sql SELECT sk.c FROM 社員書籍購入 sk
です。この列は閲覧可能な書籍を一意に示す主キーまたは外部キーが要求されます。また下段SQLでも
sql SELECT ik.c FROM 一括購入 ik
sql SELECT ik.c FROM 一括購入 ik
と同じ列が利用されています。両SQLは「書籍を特定する列」を共通で取得し、それらを集合演算(UNION 等)で重複排除する意図です。書籍を一意に識別する列は「書籍ID」ですから、cには「書籍ID」が入ります。
以上により、
a:→
b:割引率
c:書籍ID
となります。
b:割引率
c:書籍ID
となります。
誤りやすいポイント
- a を「←」と誤記する例が多いです。主キー側(書籍分類)が“1”、外部キーを持つ側(企業補助)が“多”であることを見落とさないようにしましょう。
- b に「契約割引率」「企業割引」など類似語を入れてしまうミス。属性名は実装時の列名そのものを求められているため、【問題文】中にある「割引率」をそのまま転記する必要があります。
- c を「社員書籍購入ID」や「購入ID」と混同するケース。図2の両SQLが同じ列をUNIONする設計になっている点を確認すると迷いません。
FAQ
Q: 主キー側と外部キー側の矢印方向が覚えにくいです。
A: 主キーを持つエンティティが“1”、外部キーを持つエンティティが“多”です。図では“1”から“多”へ矢印(→)を書くと覚えると混乱しにくくなります。
A: 主キーを持つエンティティが“1”、外部キーを持つエンティティが“多”です。図では“1”から“多”へ矢印(→)を書くと覚えると混乱しにくくなります。
Q: 「割引率」を数値型で登録する場合、百分率で保存すべきでしょうか。
A: 実装では 0.85 のように「掛け率」で保持すると計算が簡単です(例:定価 × 0.85)。ただし試験では型や単位までは問われていません。
A: 実装では 0.85 のように「掛け率」で保持すると計算が簡単です(例:定価 × 0.85)。ただし試験では型や単位までは問われていません。
Q: 書籍ID を複数テーブルで使う場合、命名を変えた方が良いですか。
A: 一意性を保てるならテーブル間で同じ列名を使う方が JOIN や UNION 時に可読性が上がります。本問でも同一列名「書籍ID」を採用しています。
A: 一意性を保てるならテーブル間で同じ列名を使う方が JOIN や UNION 時に可読性が上がります。本問でも同一列名「書籍ID」を採用しています。
関連キーワード: 一対多、外部キー、割引率、E-R図、集合演算
設問2:〔一括購入機能の社員割当処理の作成〕について、(1)、(2)に答えよ。
(1)d〜fに入れる適切な字句を答えよ。
模範解答
d:COUNT(*)
e:大きい
f:VALUES(:一括購入ID, :社員ID, :企業ID)
解説
解答の論理構成
-
手順2の目的
【問題文】の表2ではSELECT d
FROM 一括購入割当
WHERE 一括購入ID = :一括購入IDとあり、同じ“一括購入ID”で既に何冊割り当てられているかを求めています。行数を数えるだけなので、集計関数は COUNT() が最も自然です。したがって d には “COUNT()” が入ります。 -
手順3での比較条件
手順3の説明は
“手順1で取得した数量が、手順2で取得した数量よりe場合、手順4に進む。”
です。手順1は購入総数、手順2は現在の割当数なので、まだ割り当てられる在庫があるかどうかを判定します。割り当て可能なときは「購入数量 > 割当済数量」である必要があります。従って比較語句は “大きい” が妥当です。 -
手順4の INSERT 文
【問題文】ではINSERT INTO 一括購入割当 (一括購入ID, 社員ID, 企業ID)
fと列定義まで書かれているため、残るのは値リストです。埋込み変数 “:一括購入ID”、“:社員ID”、“:企業ID” をそのまま列順に並べれば良いのでVALUES(:一括購入ID, :社員ID, :企業ID)が正解となります。
誤りやすいポイント
- 手順2で COUNT(一括購入ID) と列名を入れてしまう
行が 0 件でも確実に 0 を返すためには COUNT(*) が安全です。 - 手順3で “小さい” や “以上” を選んでしまう
“購入数 ≦ 割当済数” では新規割当できないため比較方向を逆にしないよう注意が必要です。 - 手順4で VALUES 句を書かずにプレースホルダのみ記述
列リストがある場合でも SQL 標準どおり VALUES 句は必須です。
FAQ
Q: COUNT() と COUNT(列名) の違いはありますか?
A: COUNT() は NULL を含む全行を数えます。COUNT(列名) は列が NULL の行を除外するため、本問のように単純な行数取得には COUNT(*) が適切です。
A: COUNT() は NULL を含む全行を数えます。COUNT(列名) は列が NULL の行を除外するため、本問のように単純な行数取得には COUNT(*) が適切です。
Q: 手順3の比較で “大きい” ではなく “> を使ってはいけませんか?
A: 設問は字句(日本語)で埋める形式なので “大きい” が要求されています。実装時にはもちろん > で比較します。
A: 設問は字句(日本語)で埋める形式なので “大きい” が要求されています。実装時にはもちろん > で比較します。
Q: トランザクション制御で“専有ロック”とありますが、どのタイミングでかけるべきですか?
A: 手順1から手順4を一つのトランザクションにまとめ、最初に対象レコード(一括購入 と 一括購入割当)を読み込む際に排他ロックを取得しておくと競合を防げます。
A: 手順1から手順4を一つのトランザクションにまとめ、最初に対象レコード(一括購入 と 一括購入割当)を読み込む際に排他ロックを取得しておくと競合を防げます。
関連キーワード: COUNT関数、排他制御、INSERT文、比較演算子、プレースホルダ
設問2:〔一括購入機能の社員割当処理の作成〕について、(1)、(2)に答えよ。
(2)本文中の下線①の専有ロックを用いなかった場合、どのような問題が発生するか。30字以内で述べよ。
模範解答
一括購入数量より多い数量の書籍を割り当ててしまう問題
解説
解答の論理構成
-
問題文は「表2」の流れとして
-
手順1:SELECT 一括購入数量 FROM 一括購入 WHERE 一括購入ID = :一括購入ID
-
手順2:SELECT d FROM 一括購入割当 WHERE 一括購入ID = :一括購入ID
-
手順3: 手順1の数量が手順2の数量より小さい場合にのみ手順4へ進む
-
手順4:INSERT INTO 一括購入割当 …
と記載しています。 -
-
本処理は「一括購入数量」と「現在割り当てられている数量」を順に読み取り、条件判定後に INSERT する“典型的な在庫確認+更新”パターンです。
-
同時に複数の社員が割当依頼を行うと、各トランザクションは互いに
「手順1→手順2→手順3」を完了するまで排他されません。 -
そのため排他制御を行わなければ
- トランザクションAが手順1・2で十分な残数を確認
- ほぼ同時刻にトランザクションBも同じ確認
- 両方が手順4で INSERT を実行
という競合が発生し得ます。
-
結果として「一括購入数量」を超える件数の INSERT が行われ、問題文の助言
「①トランザクションの同時実行制御には専有ロックを用いるように」
が意味する通り、専有(排他)ロックを取得して手順1~4を1単位で実行しなければなりません。 -
以上より模範解答
「一括購入数量より多い数量の書籍を割り当ててしまう問題」
が導かれます。
誤りやすいポイント
- “SELECT 〜 FOR UPDATE”や“排他ロック”の概念を理解せず、単にコミットタイミングだけを調整すればよいと誤解しがちです。
- 「ロック粒度はページ単位でも良い」と判断し、過度なロック競合を招く実装をしてしまうケース。
- スナップショット分離(MVCC)環境下でも「読み取り一貫性があるから安全」と混同し、更新値の競合チェックを怠るケース。
FAQ
Q: なぜ共有ロックでは不十分なのですか?
A: 共有ロックは読込み専用の保護であり、他トランザクションの読込み・更新を完全には防げません。INSERT を安全に行うには対象行またはキーの範囲に対する専有(排他)ロックが必要です。
A: 共有ロックは読込み専用の保護であり、他トランザクションの読込み・更新を完全には防げません。INSERT を安全に行うには対象行またはキーの範囲に対する専有(排他)ロックが必要です。
Q: トランザクション分離レベルを SERIALIZABLE にすれば排他ロックは不要ですか?
A: 実装する DBMS により自動で範囲ロックを取得する場合もありますが、確実性と性能の両立を図るためには、業務上必要な行・範囲に明示的に専有ロックを取得する方が一般的です。
A: 実装する DBMS により自動で範囲ロックを取得する場合もありますが、確実性と性能の両立を図るためには、業務上必要な行・範囲に明示的に専有ロックを取得する方が一般的です。
Q: 専有ロックを取ると性能が心配です。代替策はありますか?
A: 代替策としては「残数を1行のカウンタに保持し、UPDATE で 残数 = 残数 - 1 AND 残数 > 0 を一括実行する」楽観的同時実行制御がありますが、問題文の想定は専有ロック方式です。
A: 代替策としては「残数を1行のカウンタに保持し、UPDATE で 残数 = 残数 - 1 AND 残数 > 0 を一括実行する」楽観的同時実行制御がありますが、問題文の想定は専有ロック方式です。
関連キーワード: 排他制御、トランザクション、ロック、同時実行、競合
設問3:
図2中のg、hに入れる適切な字句又は式を答えよ。なお、表の列名には必ずその表の相関名を付けて答えよ。
模範解答
g:UNION
h:ON ik.一括購入ID = iw.一括購入ID
解説
解答の論理構成
-
書籍一覧に含めるべき対象は次の 2 つです。
(1) 社員自身が購入したレコード
(2) 一括購入後に割り当てられたレコード
どちらも同じ列 ― 図2の sk.c と ik.c ― を取得するため、2 つの SELECT を「重複を含まない書籍の一覧」としてまとめる必要があります。 -
SQL 標準で 2 つの結果集合を重複なしで連結する演算は UNION です。
よって上段 SELECT と下段 SELECT の間の g には UNION が入ります。 -
下段 SELECT では、割り当て情報を持つ 一括購入割当 と、一括購入のマスタである 一括購入 が一体となって初めて社員への割当データになります。
したがって INNER JOIN 句で 2 表を結びつける必要があります。 -
両表の共通キーは【問題文】で明示されているとおり
「一括購入(エンティティ) ・一括購入ID … → 一括購入割当(1対多)」
です。相関名は ik(一括購入)、iw(一括購入割当)。結合条件は
ik.一括購入ID = iw.一括購入ID
となり、これが h に入る字句です。 -
以上より
g:UNION
h:ON ik.一括購入ID = iw.一括購入ID
が妥当であると論証できます。
誤りやすいポイント
- UNION ALL と誤記すると重複が除去されず要件を満たさない。
- ON iw.一括購入ID = ik.一括購入ID と左右を逆にしても結果は同じだが、相関名の書き間違いが多い。
- JOIN 句を忘れ、WHERE ik.一括購入ID = iw.一括購入ID にすると可読性が下がり、受験時に減点されやすい。
FAQ
Q: UNION と UNION ALL の使い分けは?
A: UNION は重複を除去、UNION ALL は重複を許容します。本問は「重複を含まない書籍の一覧」が要件なので UNION を選びます。
A: UNION は重複を除去、UNION ALL は重複を許容します。本問は「重複を含まない書籍の一覧」が要件なので UNION を選びます。
Q: 相関名を付け忘れると何が問題ですか?
A: 列名が重複した場合に曖昧さが生じるほか、問題文の「表の列名には必ずその表の相関名を付けて答えよ」という指示違反となり減点対象になります。
A: 列名が重複した場合に曖昧さが生じるほか、問題文の「表の列名には必ずその表の相関名を付けて答えよ」という指示違反となり減点対象になります。
Q: INNER JOIN の代わりに LEFT JOIN は使えますか?
A: 社員に割り当てられていない電子書籍は一覧対象外なので INNER JOIN が適切です。LEFT JOIN にすると不必要な行が残る可能性があります。
A: 社員に割り当てられていない電子書籍は一覧対象外なので INNER JOIN が適切です。LEFT JOIN にすると不必要な行が残る可能性があります。
関連キーワード: UNION演算、内部結合、相関名、重複排除、一括購入ID
設問4:〔書籍閲覧機能の改善〕について(1)、(2)に答えよ。
(1)本文中の下線②で追加したエンティティの属性名を全て列挙せよ。
なお、エンティティの属性名に主キーや外部キーを示す下線は付けなくてよい。
模範解答
個人会員ID, 企業ID, 社員ID
解説
解答の論理構成
-
改修の目的
問題文には、 “電子書籍リーダに個人会員IDを用いてログインした際、社員として閲覧できる書籍も一覧に追加して閲覧できるように、E-R図に新たに②一つエンティティを追加”
とあります。
つまり、既存の「個人会員」と「社員」をひも付ける仕組みが必要になります。 -
既存E-R図の状況
・個人の購入は “個人会員ID” で管理。
・社員としての購入や割当は “企業ID, 社員ID” で管理。
しかし、両者を結ぶエンティティは存在しません。 -
必要なエンティティの性質
個人会員が「どの企業のどの社員か」を示せれば、個人会員IDでログインしたときに社員側の書籍も検索できます。
従って、 “個人会員ID” + “企業ID” + “社員ID”
の3つを保持する対応表(多対多解消用の中間エンティティ)があれば要件を満たします。 -
属性列挙
上記3つのキー以外に追加情報は求められていません。よって、②で新設するエンティティの属性は
“個人会員ID, 企業ID, 社員ID”
となります。
誤りやすいポイント
- “社員名” や “企業名” など説明用の属性を追加してしまう
→ 要件は ID だけで満たせるため不要です。 - “社員ID” と “個人会員ID” のみを選び “企業ID” を抜かしてしまう
→ 社員ID は企業内で一意とは限らないため “企業ID” が必須です。 - 既存エンティティ(例:社員)に “個人会員ID” を外部キーとして追加すると考える
→ 員数(1対多/多対多)の整合が取れず、E-R 図の整形が複雑になります。
FAQ
Q: なぜ“社員ID”と“企業ID”の両方が必要なのですか?
A: 社員IDは企業ごとに採番される可能性があるため、企業を特定する“企業ID”と組にして初めて一意になります。
A: 社員IDは企業ごとに採番される可能性があるため、企業を特定する“企業ID”と組にして初めて一意になります。
Q: 3キーすべてが主キーになりますか?
A: 多対多対応の中間エンティティであるため、通常は “個人会員ID, 企業ID, 社員ID” の組を複合主キーに設定します。
A: 多対多対応の中間エンティティであるため、通常は “個人会員ID, 企業ID, 社員ID” の組を複合主キーに設定します。
Q: 関係名(エンティティ名)はどのように決めればよいですか?
A: 問題では属性名のみを問われています。実務では「個人会員社員対応」など、双方を連想できる名称を付けると分かりやすいです。
A: 問題では属性名のみを問われています。実務では「個人会員社員対応」など、双方を連想できる名称を付けると分かりやすいです。
関連キーワード: リレーションシップ、外部キー、複合主キー、多対多、正規化
設問4:〔書籍閲覧機能の改善〕について(1)、(2)に答えよ。
(2)本文中の下線③とは、どのような画面か。25字以内で述べよ。
模範解答
個人会員が企業IDと社員IDを登録する画面
解説
解答の論理構成
- 改善の目的を確認
問題文では「既存サービスを個人で利用している社員は、電子書籍リーダのログインIDを個人会員IDから企業IDと社員IDに切り替えて利用しなければならず煩雑である」と指摘しています。 - 要件の提示
その解消策として「電子書籍リーダに個人会員IDを用いてログインした際、社員として閲覧できる書籍も一覧に追加して閲覧できるように、E-R図に新たに②一つエンティティを追加し、電子書籍リーダに③一つ画面を追加」と記載されています。 - 必要な処理を整理
・個人会員IDでログインした後に企業ID/社員IDに紐づく書籍を表示するには、個人会員IDと(企業ID, 社員ID)の対応関係をどこかで保持する必要があります。
・その対応関係をユーザが登録・更新できる機能が不可欠です。 - 画面の機能を導出
追加した「③一つ画面」は、個人会員が自分のアカウントに企業情報をひも付けるための入力インタフェースであると考えると要件を満たせます。 - 結論
よって、③は「個人会員が企業IDと社員IDを登録する画面」となります。
誤りやすいポイント
- 「画面=閲覧画面」と思い込み、登録画面を想定しない。
- ②のエンティティ追加だけで自動連携できると誤解し、ユーザ入力の必要性を見落とす。
- 「企業IDと社員IDを紐付ける」対象を“管理者”と勘違いし、社員本人の登録画面と気付かない。
FAQ
Q: エンティティ追加だけでは連携できないのですか?
A: データを格納するだけでは対応関係が空のままです。ユーザが自分の企業ID/社員IDを入力する手段(画面)が必要です。
A: データを格納するだけでは対応関係が空のままです。ユーザが自分の企業ID/社員IDを入力する手段(画面)が必要です。
Q: この登録は一度行えばよいのですか?
A: 多くの場合は初回のみで構いませんが、転籍や部署異動で社員IDが変わる場合に備えて更新機能も必要になることがあります。
A: 多くの場合は初回のみで構いませんが、転籍や部署異動で社員IDが変わる場合に備えて更新機能も必要になることがあります。
Q: 管理者が一括で紐付ける運用は考えられませんか?
A: 技術的には可能ですが、個人会員IDを企業側が把握していないことが多く、プライバシー上の課題もあるため本人登録方式が採用されています。
A: 技術的には可能ですが、個人会員IDを企業側が把握していないことが多く、プライバシー上の課題もあるため本人登録方式が採用されています。
関連キーワード: アカウント連携、ユーザ入力フォーム、外部キー関連付け、ログイン認証


