基本情報技術者 2018年 秋期 午後 問03
コンサートチケット販売サイトの関係データベースの設計及び運用に関する次の記述を読んで、設問1~4に答えよ。
D社は、Web上で会員制のコンサートチケット販売サイトを運営している。販売サイトのシステムは販売サブシステムと席予約サブシステムから構成され、販売サブシステムで購入申込み及び決済を処理し、席予約サブシステムで座席指定を処理する。本問では、販売サブシステムだけを取り扱う。
販売サブシステムで利用しているデータベースの表構成とデータの格納例を図1に示す。下線付きの項目は主キーである。
〔コンサートの席の説明〕
(1) コンサートの席種には、S、A及びBがある。
(2) 各席種の価格(常に有料)及び発売席数は、コンサートごとに異なる。
〔販売サブシステムの説明〕
(1) 販売サブシステムは取り扱うコンサートの席種ごとの販売可能な席数を管理する。
(2) 会員が購入申込みを行うと、販売サブシステムは一意な販売IDを生成して販売表にレコードを追加する。
(3) 会員が支払手続を行うと、決済処理として販売サブシステムは販売IDを主キーとするレコードを決済表に追加する。ここで、決済日はレコードを追加した日とする。
(4) 販売サブシステムは決済期限日の翌日に、決済期限日を過ぎた販売表中のレコードと販売IDが同じレコードが決済表にない場合、その購入申込みは取り消されたものとして、バッチ処理によって決済表に当該販売IDを主キーとするレコードを追加する。このレコードの決済日はNULLで、決済額は−1とする。
(5) バッチ処理は、毎夜0~4時の販売サイトのシステムのメンテナンス時間帯に行う。
(6) 会員が購入を申し込んだ席数が、その時点で販売可能な席数を上回る場合には、販売サブシステムは“販売終了”と表示し、この購入申込みを受け付けない。
設問1:
問題文を見る データベースのデータの整合性を保つためにDDLで制約をつけている。図1の表構成において、列名とその列に指定する制約の正しい組合せを、解答群の中から選べ。
解答群


模範解答
ア
解説
解答の導き方
この設問では、表の各列にどの制約を付けるのが適切かを考えます。制約とは、表に誤ったデータが入らないようにするためのルールです。DDL(Data Definition Language:データベースの表や制約を定義する言語)で設定できます。
まず、決済表.決済額を考えます。通常の決済では、席の価格を支払うため決済額は正の金額になります。一方、決済期限を過ぎて購入申込みが取り消された場合は、「決済額は−1」と決められています。このように、列に入れてよい値を条件で制限するのが検査制約です。決済額には、通常の決済額か、取消しを示す−1だけが入るようにできるため、この組合せが適切です。
ほかの組合せも確認します。
- 決済表.決済日に非NULL制約を付けるのは適切ではありません。非NULL制約は、その列に必ず値を入れるルールです。しかし、取消しを表す決済表のレコードでは「決済日はNULL」と決められています。NULLは、値が設定されていない状態を表します。
- 商品詳細表.席種に参照制約を付けるのも適切ではありません。参照制約は、ある表の値が別の表に登録されている値を参照するようにするルールです。図1には席種だけを管理する別の表はなく、席種には「S、A及びB」があります。この列には、決められた値だけを認める検査制約が適しています。
- 販売表.会員IDに一意性制約を付けるのも適切ではありません。一意性制約は、同じ値を複数の行に登録できないようにするルールです。同じ会員が複数回購入を申し込むことはあり得るため、会員IDが販売表で重複する場合があります。購入申込みごとに一意に生成される販売IDのように、会員IDとは別の列で申込みを区別します。
このため、決済表.決済額に検査制約を指定する組合せが正解です。
誤りやすいポイント
- 「値を決められた範囲にする制約」と「別の表にある値を参照させる制約」を混同しないようにします。前者は検査制約、後者は参照制約です。
- NULLは、必ずしも入力漏れではありません。この問題では、取消しを表すレコードの決済日として意図的に使われます。
- 会員IDは会員を識別するための値ですが、販売表では同じ会員が複数の申込みを行えるので、一意とは限りません。
FAQ
Q: 検査制約と参照制約は、どう違いますか?
A: 検査制約は、列に入る値が条件を満たすかを確認します。参照制約は、ある列の値が別の表に存在する値を指しているかを確認します。
A: 検査制約は、列に入る値が条件を満たすかを確認します。参照制約は、ある列の値が別の表に存在する値を指しているかを確認します。
Q: 決済日がNULLのレコードは、データの誤りではないのですか?
A: この問題では、購入申込みの取消しを表すために決済日をNULLにすると定められています。そのため、決済日に非NULL制約を付けると、正しい取消しレコードを登録できません。
A: この問題では、購入申込みの取消しを表すために決済日をNULLにすると定められています。そのため、決済日に非NULL制約を付けると、正しい取消しレコードを登録できません。
Q: 会員IDが会員を識別する値なら、販売表でも一意にするのですか?
A: いいえ。会員IDは会員を識別しますが、販売表には同じ会員の申込みが複数行登録される可能性があります。したがって、販売表の会員IDには一意性制約を付けられません。
A: いいえ。会員IDは会員を識別しますが、販売表には同じ会員の申込みが複数行登録される可能性があります。したがって、販売表の会員IDには一意性制約を付けられません。
関連キーワード: 検査制約、非NULL制約、参照制約、一意性制約、DDL
設問2:
問題文を見る “販売終了”の表示判定を行うために、販売できない席数を求める必要がある。販売できない席数を出力するSQL文のに入れる正しい答えを、解答群の中から選べ。ここで、コンサートIDはC00001、席種はSである。a1とa2に入れる答えは、aに関する解答群の中から組合せとして正しいものを選ぶものとする。
SELECT SUM(販売表.席数)
FROM 販売表 a1 決済表 ON 販売表.販売ID = 決済表.販売ID
WHERE 販売表.コンサートID = 'C00001'
AND 販売表.席種 = 'S'
AND a2
aに関する解答群


模範解答
a:エ
解説
解答の導き方
このSQLでは、席種ごとの「販売できない席数」を求めるために、すでに申込みによって確保されている席数を合計します。申込みが取り消されていない席は、まだ決済が済んでいなくても合計に含めます。
まず、販売表と決済表の関係を整理します。本文では、支払手続を行うと決済表にレコードが追加され、期限を過ぎても支払われなかった申込みは、バッチ処理で決済表に「決済額は−1」のレコードが追加されると説明されています。一方、期限前でまだ支払手続をしていない申込みには、決済表に対応するレコードがありません。
そのため、申込みの状態ごとの扱いは次のようになります。
ここでNULLは、値が入っていないことを表すデータベース上の特別な値です。
未決済の申込みも残すには、販売表を基準にして、決済表に対応するレコードがあれば結び付け、なければ販売表の行を残す必要があります。この結合方法がLEFT OUTER JOIN(左側の表の行をすべて残す結合)です。したがって、a1にはLEFT OUTER JOINが入ります。
INNER JOIN(両方の表に対応する行がある場合だけ残す結合)では、決済表にレコードがない未決済の申込みが結果から外れてしまいます。未決済でも席を確保している申込みを数えるため、これは適しません。RIGHT OUTER JOINでは決済表側の行を残すことになり、決済表に行がない未決済の申込みを取りこぼします。
次に、a2の条件を考えます。決済表の決済額がNULLなら、決済表に対応するレコードがない未決済の申込みなので、席数に含めます。また、決済額が0以上なら支払済みの申込みなので、これも含めます。一方、決済額が−1のレコードは取消しを表すため、合計から除外します。
この条件に当てはまるのは、次の式です。
(決済表.決済額 IS NULL OR 決済表.決済額 >= 0)
したがって、a1とa2の組合せはエです。SUMは、指定された席数を合計する集約関数(複数の行の値をまとめて計算する関数)です。
誤りやすいポイント
- INNER JOINを選ぶと、決済表にまだレコードがない未決済の申込みが除外されます。期限前の申込みも席数に含める必要があります。
- 取消しのレコードは、決済日がNULLでも決済額は−1です。決済日がNULLかどうかだけでは、未決済と取消しを区別できません。
- 取消しを除外する条件は、決済額が−1ではない行を選ぶことです。選択肢では、未決済のNULLと支払済みの0以上を合わせて指定する条件が必要です。
- RIGHT OUTER JOINは、販売表ではなく決済表側の行を残します。決済表に行がない未決済の申込みを数えられません。
FAQ
Q: 未決済の申込みを、なぜ販売できない席数に含めるのですか?
A: 本文では、申込みを受け付ける際に、その時点で販売可能な席数を上回る申込みを受け付けないとしています。申込み後、期限切れによる取消しが決まるまでは席を確保しているため、未決済でも席数に含めます。
A: 本文では、申込みを受け付ける際に、その時点で販売可能な席数を上回る申込みを受け付けないとしています。申込み後、期限切れによる取消しが決まるまでは席を確保しているため、未決済でも席数に含めます。
Q: LEFT OUTER JOINで結合先がないと、なぜ決済額がNULLになるのですか?
A: 販売表の行は残りますが、対応する決済表の行が存在しないため、決済表側の項目に値がありません。その状態をNULLで表します。
A: 販売表の行は残りますが、対応する決済表の行が存在しないため、決済表側の項目に値がありません。その状態をNULLで表します。
Q: 決済額が0のレコードも含めてよいのですか?
A: 選択肢では「決済額 >= 0」が支払済み側の条件です。取消しを表す−1だけを除き、0以上のレコードを含める形になります。
A: 選択肢では「決済額 >= 0」が支払済み側の条件です。取消しを表す−1だけを除き、0以上のレコードを含める形になります。
関連キーワード: 外部結合、内部結合、NULL、集約関数、主キー
設問3:
問題文を見る 決済期限日まで残り3日となっても支払手続が行われていない購入申込みがある会員に、支払手続を促す電子メールを送る。この会員の氏名、電子メールアドレス及び販売IDを出力するSQL文のに入れる正しい答えを、解答群の中から選べ。NOWはSQLを実行した日の日付を返すユーザ定義関数であり、DATEDIFFはともに日付である二つの引数を受け取って第1引数から第2引数を引いた日数を整数値で返すユーザ定義関数である。
SELECT 会員表.氏名, 会員表.電子メールアドレス, 販売表.販売ID
b
bに関する解答群
ア:FROM 会員表, 販売表
WHERE DATEDIFF(販売表.決済期限日, NOW()) = 3
AND 販売表.会員ID = 会員表.会員ID
AND 販売表.販売ID NOT IN (SELECT 販売ID FROM 決済表)
イ:FROM 会員表, 販売表
WHERE DATEDIFF(販売表.決済期限日, NOW()) = 3
AND 販売表.会員ID = 会員表.会員ID
AND 販売表.販売ID IN
(SELECT 販売ID FROM 決済表 WHERE 決済額 >= 0)
ウ:FROM 会員表, 販売表, 決済表
WHERE DATEDIFF(販売表.決済期限日, NOW()) = 3
AND 販売表.会員ID = 会員表.会員ID
AND 販売表.販売ID = 決済表.販売ID
エ:FROM 会員表, 販売表, 決済表
WHERE DATEDIFF(販売表.決済期限日, NOW()) = 3
AND 販売表.会員ID = 会員表.会員ID
AND 販売表.販売ID = 決済表.販売ID
AND 決済表.決済額 <> -1
模範解答
b:ア
解説
解答の導き方
この設問では、決済期限日まであと3日で、まだ支払手続が行われていない購入申込みについて、会員の氏名・電子メールアドレスと販売IDを取り出すSQLを選びます。SQL(Structured Query Language:データベースに問い合わせるための言語)の条件を、必要な情報ごとに整理します。
-
必要な情報を持つ表を選びます。
会員の氏名と電子メールアドレスは会員表にあります。販売IDと決済期限日は販売表にあります。そのため、会員表と販売表を使います。 -
販売表の申込みを、該当する会員に対応付けます。
販売表には会員IDがあるので、会員表の会員IDと一致させます。複数の表を共通の項目で対応付けることを、表の結合といいます。解答群アの「販売表.会員ID = 会員表.会員ID」が、この対応付けをしています。 -
期限日まで残り3日かを確認します。
問題文では、DATEDIFFは第1引数から第2引数を引いた日数を返すと説明されています。したがって、DATEDIFF(販売表.決済期限日, NOW())は「決済期限日 − 実行日」です。これが3なら、決済期限日まで3日あります。 -
支払手続が行われていない申込みを選びます。
支払手続が行われると、販売表と同じ販売IDのレコードが決済表に追加されます。一方、販売期限を過ぎた申込みの取消しについても、問題文には「決済期限日の翌日に」決済表へレコードを追加するとあります。つまり、決済期限日まであと3日の申込みなら、取消しを示すレコードもまだ追加されていません。そのため、決済表に同じ販売IDがないことを条件にします。解答群アの「販売表.販売ID NOT IN (SELECT 販売ID FROM 決済表)」は、決済表にその販売IDが存在しない申込みを選びます。この条件が、支払手続が行われていない申込みに合います。
以上から、期限日までの日数、会員との対応付け、決済表に販売IDがないことをすべて確認しているアが適切です。
誤りやすいポイント
- 決済表にレコードがあることを、支払済みと決めつけないこと
決済表には、支払手続によるレコードだけでなく、期限を過ぎて取り消された申込みのレコードも追加されます。取消しの場合は決済額が−1です。 - 「決済表にない」と「決済額が−1ではない」を混同しないこと
この設問で探すのは、支払済みでも取消し済みでもない、決済表にまだ販売IDがない申込みです。決済表のレコードを探す条件ではありません。 - DATEDIFFの引き算の順序に注意すること
この問題では第1引数から第2引数を引きます。決済期限日を先に、NOWを後に指定することで、期限日までの日数を調べています。 - 会員表と販売表の対応付けを忘れないこと
販売表の会員IDと会員表の会員IDを結び付けないと、販売IDに関係のない会員の氏名や電子メールアドレスが表示される可能性があります。
FAQ
Q: 決済表に販売IDがないことを調べるのはなぜですか。
A: 支払手続が行われると、その販売IDのレコードが決済表に追加されるためです。決済表に販売IDがなければ、支払手続がまだ行われていないと判断できます。
A: 支払手続が行われると、その販売IDのレコードが決済表に追加されるためです。決済表に販売IDがなければ、支払手続がまだ行われていないと判断できます。
Q: 決済額が−1のレコードを除く条件ではだめですか。
A: −1のレコードは取消しを表しますが、支払済みのレコードも決済表にあります。さらに、決済表との結合条件を置くと、決済表にレコードがある申込みだけが対象になります。今回は、決済表にレコードがない申込みを探す必要があります。
A: −1のレコードは取消しを表しますが、支払済みのレコードも決済表にあります。さらに、決済表との結合条件を置くと、決済表にレコードがある申込みだけが対象になります。今回は、決済表にレコードがない申込みを探す必要があります。
Q: DATEDIFFの引数を逆にするとどうなりますか。
A: 第1引数から第2引数を引くため、期限日と実行日の順序を逆にすると日数の符号も逆になります。この設問では、期限日から実行日を引く形にして、期限日までの日数を求めます。
A: 第1引数から第2引数を引くため、期限日と実行日の順序を逆にすると日数の符号も逆になります。この設問では、期限日から実行日を引く形にして、期限日までの日数を求めます。
関連キーワード: SQL、表結合、サブクエリ、NOT IN、日付関数
設問4:
問題文を見る 会員への優待サービスのために、ポイント制度を導入する。そのために修正した会員表、決済表及び販売表の表構成を図2に示す。ポイント制度を導入するときに追加した列は0で初期化する。

会員は購入申込み時に、1ポイント1円としてポイント残高の範囲で、販売額に充当するポイント数を指定する。販売サブシステムは、指定したポイント数を使用ポイントに格納し、ポイント残高から減じる。会員は、販売額から使用ポイントを差し引いた金額を決済額として支払う。販売額の全額にポイントを充当した場合は、販売サブシステムは購入申込み時に支払手続が行われたものとし、決済処理として、決済表にレコードを追加する。
ポイント制度の導入時に追加したバッチ処理によって、前日に決済処理された販売IDごとに、その決済額が20,000円以上、10,000円以上20,000円未満、10,000円未満の場合に、それぞれ3%、2%、1%のポイントを付与する。付与したポイント数は、付与ポイントに格納し、ポイント残高に加える。
決済表の付与ポイントを更新する正しいSQL文を、解答群の中から選べ。NOW, DATEDIFFは設問3で使用したユーザ定義関数と同じであり、FLOORは引数の値以下で最大の整数値を返す関数である。
解答群
ア:INSERT INTO 決済表( 付与ポイント )
SELECT IF 決済額 >= 20000 THEN FLOOR(決済額 * 0.03)
ELSEIF 決済額 >= 10000 THEN FLOOR(決済額 * 0.02)
ELSE FLOOR(決済額 * 0.01) END
WHERE DATEDIFF(NOW(), 決済日) = 1
イ:UPDATE 決済表 SET 付与ポイント = (
CASE 決済額 >= 20000 THEN FLOOR(決済額 * 0.03)
WHEN 決済額 >= 10000 THEN FLOOR(決済額 * 0.02)
ELSE FLOOR(決済額 * 0.01) END )
WHERE DATEDIFF(NOW(), 決済日) = 1
ウ:UPDATE 決済表 SET 付与ポイント = (
CASE WHEN 決済額 >= 20000 THEN FLOOR(決済額 * 0.03)
WHEN 決済額 >= 10000 THEN FLOOR(決済額 * 0.02)
ELSE FLOOR(決済額 * 0.01) END )
WHERE DATEDIFF(NOW(), 決済日) = 1
エ:UPDATE 決済表 SET 付与ポイント = (
IF 決済額 >= 20000 THEN FLOOR(決済額 * 0.03)
ELSEIF 決済額 >= 10000 THEN FLOOR(決済額 * 0.02)
ELSE FLOOR(決済額 * 0.01) END )
WHERE DATEDIFF(NOW(), 決済日) = 1
模範解答
ウ
解説
解答の導き方
この設問では、前日に決済されたレコードに対して、決済額に応じたポイントを付与するSQLを選びます。SQL(Structured Query Language:データベースのデータを操作するための言語)で、どのレコードを対象にし、どのようにポイント数を決めるのかを順に確認します。
1. 既存のレコードを更新する処理か、新しいレコードを追加する処理かを確認します。
問題文には、付与したポイント数を「付与ポイントに格納」するとあります。ポイントを付与する対象は、すでに決済表にあるレコードです。そのレコードの値を書き換えるので、既存データを更新するUPDATE文を使います。
2. 前日に決済されたレコードだけに絞ります。
問題文では、対象を「前日に決済処理された販売IDごと」としています。選択肢のWHERE句は、処理対象のレコードを絞る条件を書く部分です。
DATEDIFF(NOW(), 決済日) = 1は、現在の日付と決済日の差が1日であるレコードを対象にします。したがって、前日に決済されたレコードに絞れます。
また、取消しの場合は決済日がNULLです。NULLの日付との差は1日とは判定されないので、取消しのレコードはこの条件に一致しません。
3. 決済額に応じてポイント数を分けます。
問題文の条件を整理すると、次のとおりです。
条件分岐を表すCASE式(条件に応じて異なる値を選ぶ式)では、次の形を使います。
CASE WHEN 条件 THEN 値
WHEN 条件 THEN 値
ELSE それ以外の値 END
最初に「決済額が20,000以上か」を調べ、そうでなければ「10,000以上か」を調べます。どちらにも当てはまらなければ、最後のELSEの計算を使います。条件の順番がこのようになっているため、20,000以上の決済額に2%が適用されてしまうこともありません。
FLOORは、小数部分を切り捨てて整数にする関数です。例えば、決済額が19,999円なら、2%は399.98です。ポイント数は整数なので、FLOORによって399になります。
4. 構文と処理内容の両方を満たす選択肢を選びます。
次のSQLは、UPDATE文で既存の決済表のレコードを更新し、CASE式で付与ポイントを決め、WHERE句で前日のレコードだけに絞っています。
UPDATE 決済表 SET 付与ポイント = (
CASE WHEN 決済額 >= 20000 THEN FLOOR(決済額 * 0.03)
WHEN 決済額 >= 10000 THEN FLOOR(決済額 * 0.02)
ELSE FLOOR(決済額 * 0.01) END )
WHERE DATEDIFF(NOW(), 決済日) = 1
この条件を満たすのは選択肢ウです。
誤りやすいポイント
- アは、既存のレコードを更新するのではなく、新しいレコードを追加しようとしています。 INSERT文は新しいレコードを追加する処理です。今回は、決済表にすでにあるレコードの付与ポイントを更新するため、処理の種類が合いません。
- イは、CASE式の書き方が正しくありません。 条件を直接並べる形式では、CASEの後にWHENを置きます。選択肢イはCASEの後に条件を書いているため、正しい形ではありません。
- エのIF ... THEN ... ELSEIF ... ENDは、このように値を返す式として使う書き方ではありません。 選択肢ウのように、CASE WHEN ... THEN ... ENDの形にします。
- 決済額の条件を確認する順番にも注意が必要です。 20,000円以上を先に判定することで、該当するレコードに3%を適用できます。
FAQ
Q: 決済日がNULLの取消しレコードにもポイントが付くことはありますか?
A: ありません。DATEDIFF(NOW(), 決済日)の結果がNULLになるため、WHERE句の「= 1」を満たさず、更新対象になりません。
A: ありません。DATEDIFF(NOW(), 決済日)の結果がNULLになるため、WHERE句の「= 1」を満たさず、更新対象になりません。
Q: 決済額が10,000円未満の場合は、必ず1ポイント以上もらえますか?
A: いいえ。1%を計算した結果が1未満なら、FLOORで小数部分が切り捨てられます。例えば、決済額が0円なら計算結果も0ポイントです。
A: いいえ。1%を計算した結果が1未満なら、FLOORで小数部分が切り捨てられます。例えば、決済額が0円なら計算結果も0ポイントです。
Q: CASE式は、なぜ金額の大きい条件から調べるのですか?
A: 条件が上から順に判定され、最初に当てはまった結果が使われるからです。20,000円以上を先に判定すれば、3%を適用する対象を先に確定できます。
A: 条件が上から順に判定され、最初に当てはまった結果が使われるからです。20,000円以上を先に判定すれば、3%を適用する対象を先に確定できます。
関連キーワード: SQL、UPDATE文、CASE式、WHERE句、NULL




