Zenn
Agent Grow Tech Notes

【応用情報】午後『データベース』のリレーションを制する!第2回『必ず1対1』編

2025/02/13に公開

はじめに

今回は、応用情報技術者試験午後『問6:データベース』 に出題されることが多い(ほぼ毎回)リレーションを回答する際のポイントについて書きます。
この記事は続編(2回目) となっております。
まずは前回の記事を読んで頂くことで、何をやろうとしているのかが分かると思います。

今回のサンプル

前回と同じ過去問を使って解説をします。
※出典:令和6年度 春期 応用情報技術者試験 午後 問題冊子

簡単におさらい(1つ目:『必ず1対多』)

前回はリレーションが『必ず1対多』になるパターンを紹介しました。
テーブル結合時、結合するキー項目以外にも主キーを持つ側が『多(矢の先端)』になるのでしたね。

2つ目:必ず『1対1』になるパターン

ここではテーブル『従業員』と『退職』を見ましょう。

実線のアンダーラインが主キーです。

  • テーブル『従業員』の主キーは『会社番号』と『従業員番号
  • テーブル『退職』の主キーは『会社番号』と『従業員番号

はい、この時点で確定です。理由は

  • テーブル『従業員』と『退職』の主キーの組合せが全く同じ項目である。
  • 1つのテーブル内に、同じ主キーの組合せ複数レコード持つことはできない
  • 結合する主キーの組合せが片方は1件で、もう片方が複数とはならない。
  • つまり、『1件ー1件』だから『1対1』。

実際のデータをイメージしてみよう

上記の説明だけでは分かり辛いので、実際にテーブル定義を作ってみましょう。
まず、データはこのようなイメージです。
テーブル『従業員』

会社番号(主キー) 従業員番号(主キー) 従業員氏名 部署番号 入社年月日 退職年月日
1001 111111 幕張太郎 44 2001/4/1 2019/3/31
1001 222222 稲毛花子 44 2013/10/1 NULL
9009 111111 チーバクン 77 2022/4/1 2024/3/31

テーブル『退職』

会社番号(主キー) 従業員番号(主キー) 在籍期間 退職理由
1001 111111 216 家庭の事情
9009 111111 24 ご当地キャラになるため

まず、テーブル同士でリレーション(結合)できる項目は『会社番号』と『従業員番号』です。
どちらのテーブルも、その組合せは主キーなので重複していないですよね?
重複していない同士を結合するので『していない同士』、つまり『1対1』(くどいw)

ちなみに、テーブル『退職』の方は1レコード足りません。
これは稲毛花子さん退職していないので、退職理由がないためです。
退職理由にNULLを設定し、レコード数を合わせる方法でも良いのですが、次の説明をしたいのであえてこのようにしました。

ゼロを許容するかしないか

今回題材にしている過去問のER図は 『1対1』や『1対多』は表現できています
しかし『そもそもお互いのデータが必ず存在する必要はないのか?』、つまり『1対0』や『0対多』を許すかが表現できません
その表現について、ここでは『ゼロ表記あり』といった書き方をしておきます。
応用情報技術者試験では滅多に出題されませんが、出題されたことはあります
詳しくは、私の過去の記事や下記の過去問解説動画をご参照下さい。

https://youtu.be/KnA2blC2KrQ

脱線しかけていますが、結論としては『今回のサンプルはゼロが許容される』ことになります。
なので、上記のデータはそのままとします。

SQLを書いて動かしてみよう

前回に続き、今回も実際にSQLを書いて実際に結合する様子を見てみましょう。
PostgreSQLで書きますので、よろしければ試してみてください。
※簡単に試す方法はこちらの記事を参考にして下さい。

まず、先程のデータを作ります。

-- 従業員テーブルを作る
CREATE TABLE 従業員 (
    会社番号     INTEGER
  , 従業員番号   INTEGER
  , 従業員氏名   VARCHAR
  , 部署番号     INTEGER
  , 入社年月日   DATE
  , 退職年月日   DATE
  , PRIMARY KEY(会社番号,従業員番号) -- 主キー
);
-- 従業員テーブルにデータを登録
INSERT INTO 従業員 VALUES
(1001,111111,'幕張太郎',44,'2001/4/1','2019/3/31')
,(1001,222222,'稲毛花子',44,'2013/10/1',NULL)
,(9009,111111,'チーバクン',77,'2022/4/1','2024/3/31');
-- 退職テーブルを作る
CREATE TABLE 退職 (
    会社番号     INTEGER
  , 従業員番号   INTEGER
  , 在籍期間     INTEGER
  , 退職理由     VARCHAR
  , PRIMARY KEY(会社番号,従業員番号) -- 主キー
);
-- 退職テーブルにデータを登録
INSERT INTO 退職 VALUES
(1001,111111,216,'家庭の事情')
,(9009,111111,24,'ご当地キャラになるため');

では、リレーションを張って両方のテーブルからデータを取得するSELECT文を発行します。
例として『誰が、いつ、何で退職したの?』という要件に応えるとします。

-- 従業員の退職日と退職理由を取得
-- 退職していない従業員を含むバージョン
SELECT
     ju.会社番号
    ,ju.従業員番号
    ,ju.従業員氏名
    -- 退職年月日が無い場合はダミー日付
    ,COALESCE(ju.退職年月日,'2999/12/31') AS 退職年月日
    -- 退職理由がNULLの場合は『※在籍中』
    ,COALESCE(ta.退職理由,'※在籍中') AS 退職理由
FROM 従業員 ju
-- 左外部結合:従業員テーブルに存在するレコードは紐づかなくても出力する
LEFT OUTER JOIN 退職 ta
           ON   ju.会社番号 = ta.会社番号
           AND  ju.従業員番号 = ta.従業員番号
ORDER BY ju.会社番号 ,ju.従業員番号;

実行すると、このような結果になります。
在籍有無に関わらず全員を表示した中から退職情報を見たい場合です。

もう1つのパターンもやってみます。
こちらの方が、より現実的な要件かもしれないです。

-- 従業員の退職日と退職理由を取得
-- 退職した従業員のみ取得するバージョン
SELECT
     ju.会社番号
    ,ju.従業員番号
    ,ju.従業員氏名
    ,ju.退職年月日
    ,ta.退職理由
FROM 従業員 ju
-- 内部結合:お互いに紐づくレコードのみ出力する
INNER JOIN 退職 ta
           ON   ju.会社番号 = ta.会社番号
           AND  ju.従業員番号 = ta.従業員番号
ORDER BY ju.会社番号 ,ju.従業員番号;

退職者だけになりました。
稲毛花子さんは在籍中なので、この結果には出力されません。

さいごに

いかがでしたか?
「1対1を説明するだけなのに、これは流石にやり過ぎでは?
と思うかもしれません。
前回も同じことを書いてますが、実務で役立てるには本質を理解する必要があるので、このくらい面倒な方がちょうど良いのでは?と思います。

そして、まだ続きがあります(笑)
次回以降は ER図を見ただけでは判断ができない場合を解説する予定です。
また懲りずに付いてきていただけると嬉しいです^^;

では、今回は以上です。
ここまでお付き合い頂き、ありがとうございましたm(_ _)m

Agent Grow Tech Notes
Agent Grow Tech Notes

Discussion

ログインするとコメントできます