2025-4I データベース工学 第07回 講義資料

2025年11月20日 (木) 3-4時限

1 連絡と準備

1.1 後期中間試験に関する連絡

後期中間試験 (筆記試験) は、11月28日 (金) の2時限目に試験時間 60分 で実施します。

1.1.1 持ち込み品について

img

1.1.2 出題内容と範囲について

1.1.3 SQLの記述について

1.2 ハンズオン学習の準備

次の手順でSQL演習環境の立ち上げと、教材の更新を取得してください。

1.3 今回の講義内容

前回の講義では、データベース設計の「概念設計」と「論理設計」を取り上げ、IE記法 (Crow’s Foot記法) によって「概念ER図」と「論理ER図」を作成する手順と、その具体的な読み取り方、作図法について学びました。この際、論理ER図が適切に設計されていれば 結果的に RDB として自然に「正規化」されたテーブル構造になっていること がほとんどです。

ただし、必ずしも十分に正規化されているとは限らないため、今回の講義では、データの 正規化 (Normalization) という観点から、「RDBのテーブル設計」について詳しく学んでいきます。

また、物理設計の基礎についても学び、実際に RDB にテーブルを作成するための CREATE 文についても学んでいきます。

なお、正規化は IPAの各種試験 (基本情報試験や応用情報試験) においても、知識や理解が問われるもの (頻出のテーマ) になります。この講義のなかで、しっかりと学んでおくことをお勧めします。

1.3.1 参考: 基本情報技術者試験過去問道場

基本情報技術者試験過去問道場では、当該試験の過去問に挑戦することができます。

2 正規化 (概要)

データベース設計における 正規化 (Normalization) とは、データの冗長性を排除し、効率的かつ一貫性が保たれるようにテーブルを設計 するための概念、および、その具体的な作業を意味します。これは、エンティティの持つ情報を失うことなく、無損失分解の原則(=分解しても元の情報を完全に復元できるという原則)に従い、1事実1箇所 (1 fact in 1 place) となるようにテーブルを分解していく作業になります。そして、その結果として得られるテーブル構造を 正規形 (Normal Form) と呼びます。

正規形には、いくつかの段階 (レベル) があります。非正規形からはじまり、第1正規形、第2正規形、第3正規形、ボイス・コッド正規形 (第3.5正規形)、第4正規形、第5正規形 へと整理していきます。ただし、実務上は 第3正規形 までで十分とされることが多く、本科目でも「第3正規形」までを扱います。なお、第1正規形 (First Normal Form) は「1NF」のように略表記されることがあるので覚えておいてください。同様に 2NF3NF… と表現されます。

整理すると以下のようになります。以下、これを詳しくみていきます。

(プロンプト例)

RDB設計に関する質問です。テーブルが正規形になっていないと何か困ることがあるのですか。簡単な例を示して教えてください。

2.0.1 定着確認

3 非正規形

非スカラー値 (=複合値、つまり、単一ではない値) を含むカラムが存在したり、列方向に同種のデータが繰り返されている構造のテーブルは 非正規形 (=第1正規形(1NF)を満たしていない) とよばれます。

たとえば、次のようなテーブルは、典型的な 非正規形 (Unnormalized Form) となります。テーブル上で、主キーは 下線 で示しています。

▼ 非正規形テーブル1

character_id name job skill
1 Alice Priest Heal, Resurrection
2 Bob Monk DragonKick
3 Charlie Wizard Fire, Thunder, Venom
4 Ellen Wizard Thunder

▼ 非正規形テーブル2

character_id name item1 qty1 item2 qty2 item3 qty3
1 Alice Map 1 Potion 5
2 Bob Ring 3
3 Charlie Wand+1 1 Scroll 4 Potion 3
4 Ellen

非正規形テーブル1 は、skill カラムに 非スカラー値 (複合値) を含んでいます。このようなテーブル設計にすると、個々の skill を独立したデータとして扱うことができなくなり、検索や更新における扱いが煩雑 になります。たとえば「スキルとして Heal を使えるキャラクターを検索したい」といった単純な問い合わせでも、LIKE述語を使用した部分一致検索 (WHERE skill LIKE '%Heal%') が必要になり、検索や更新に際して、正確性やパフォーマンスに関して考慮すべきことが多くなります

カラムに複合値を入れると検索精度や更新容易性が損なわれる理由

単純な部分一致検索 LIKE '%Heal%' では MegaHeal や HealBlock のように、名前の一部に Heal を含むスキルを含むレコードを抽出してしまう可能性があります。

また、スキルの一部だけを更新したい場合 (たとえば Fire, Thunder, VenomQuake を追加するような処理) に、文字列の部分編集や置換操作が必要となります。

また、非正規形テーブル2 では、同じ種類の情報 (アイテムとその数量) が列方向に繰り返されています。これも、第1正規形 (1NF) に違反する典型的なパターンとなります。このようなテーブル設計は「所持可能なアイテムを4種類に増やしたい」といった仕様変更に対して、新しいカラムの追加 (=テーブルスキーマの変更) が必要となるという問題があります。

レコード追加とカラム追加は何が違うのか

RDB において INSERT による「レコードの追加・削除」は DML操作 に属し、比較的軽い処理になります。これに対して「カラムの追加・削除」は ALTER を用いた DDL操作 は、テーブル構造そのもの (テーブルのスキーマ) を書き換える処理となるため、負荷も時間も大きくなります。

さらに、スキーマの変更は、既存 SQL の前提を崩すおそれがあるため慎重に実行する必要があります (既存の SQL がカラム名や構造をどのように参照しているかを全て確認する必要があります)。このため、運用開始後にカラム追加が必要になる状況を極力避けられるようにテーブルを設計することが重要になります。

また、非正規形テーブル2 のようなテーブル構造は、大多数のキャラクタがアイテムを1種しか所持しないようなケース (=ほとんどのカラムが NULL となるようなケース) では、ストレージを無駄に消費することになります。さらに、SUM()COUNT() を使った集計処理を行う際にも、列を横断的に扱わねばならず、SQLが複雑化し保守性や性能面で問題を抱える設計となります

(プロンプト例)

RDBに関する質問です。ワイルドカードを使用した部分一致検索は、インデックスを利用できないため、全件走査 (フルスキャン) が必要であると聞きました。そもそもインデックスとは何ですか。

3.0.1 定着確認

4 第1正規化

第1正規化、つまり、非正規形を第1正規形に整えるには、複合値や列方向 (横方向) に繰り返されている情報行方向 (縦方向) に展開して保持する ようにします。

具体的には、以下のようにします。

▼ 先ほどの「非正規形テーブル1」を「第1正規化」したテーブル

character_id name job skill
1 Alice Priest Heal
1 Alice Priest Resurrection
2 Bob Monk DragonKick
3 Charlie Wizard Fire
3 Charlie Wizard Thunder
3 Charlie Wizard Venom
4 Ellen Wizard Thunder

これにより、第1正規形の要件である「各カラムがスカラー値のみを格納し、列方向の繰り返しが排除されている」を満たした構造となりました。

ただし、このままで character_id という「主キー」だけでは、レコードを一意に識別できない構造となってしまっているため、以下に示すように skill_id カラムを追加して「複合主キー」を構成します。

▼ 「非正規形テーブル1」を「第1正規形」にして「複合主キー」を設定したテーブル

character_id name job skill_id skill
1 Alice Priest 1 Heal
1 Alice Priest 2 Resurrection
2 Bob Monk 3 DragonKick
3 Charlie Wizard 4 Fire
3 Charlie Wizard 5 Thunder
3 Charlie Wizard 6 Venom
4 Ellen Wizard 5 Thunder

同様に、さきほどの 非正規形テーブル2 についても、第1正規化して、さらに複合主キー (character_iditem_id) を設定すると以下のようになります。

character_id name item_id item qty
1 Alice 1 Map 1
1 Alice 2 Potion 5
2 Bob 3 Ring 3
3 Charlie 4 Wand+1 1
3 Charlie 5 Scroll 4
3 Charlie 2 Potion 3
4 Ellen

なお、上記の item_id のように、本来は主キー (複合主キーを含む) に NULL (空欄) を含めること はできません。しかし、ここでは説明の都合上 (=Ellen のレコードを残しておくために)、一時的に許容しています。この問題は、第2正規化のなかで解決していきます。

なお、第1正規形にしたことで「各キャラクタが所持可能なアイテム種は最大で3つまで」といった制約が無くなってしまいましたが、一般にこれらの制約は RDB を利用するアプリケーション側 で管理・制御します。たとえば、INSERT の実行前に SELECT COUNT(*) で現在の所持数を確認するなどの方法で対応します。

4.0.1 定着確認

book_id title author1 author2 author3
1 New Python Alice Ellen
2 React 1 Bob
3 React 2 Bob Alice Charlie
4 PostgreSQL Charlie Ellen

5 第2正規化

第1正規形の要件を満たし、複合主キーの一部に対する 部分関数従属 が排除 されている構造を 第2正規形 といいます。

まずは、関数従属 という概念について解説し、その後、第2正規化を解説します。なお、「関数従属」という用語は、資格試験などでも頻出するのでしっかりと理解しておいてください。

5.1 関数従属

関数従属 (FD: Functional Dependency) とは…

ある属性集合 \(\mathrm{X}\) の値が決まると、別の属性集合 \(\mathrm{Y}\) の値も一意に定まる

…という「属性間の関係を表す概念」になります。

このような関係があるとき「\(\mathrm{Y}\)\(\mathrm{X}\) に関数従属する」と言い、記号では「 \(\mathrm{X}\rightarrow\mathrm{Y}\) 」や「 \(\{x_1,\ x_2\}\rightarrow\{y_1,\ y_2,\ y_3\}\) 」のように表現します。ただし \(\mathrm{X}=\{x_1,\ x_2\}\)\(\mathrm{Y}=\{y_1,\ y_2,\ y_3\}\) とします。

RDB のテーブル設計の文脈では、「あるカラム集合 \(\mathrm{X}\) の値」が決まると「別のカラム集合 \(\mathrm{Y}\) の値」が一意に決まるようなとき\(\mathrm{Y}\)\(\mathrm{X}\) に関数従属する 」といいます。単に「\(\mathrm{Y}\)\(\mathrm{X}\) に従属する」と表現する場合もあります。

たとえば、次のようなテーブルがあるとします。

character_id name job_id job level guild
1 Alice J5 Priest 42 Yamato
2 Bob J2 Monk 33 hameln
3 Charlie J6 Wizard 57
4 Ellen J6 Wizard 51

このとき、次のようなことが言えます。

なお、テーブルのなかに格納されている現時点のレコードだけに着目すれば…

character_id, name, job_id, job, guild は、level に関数従属する

…のように判断できそうですが、これは誤りです。

正規化に関して、関数従属とはすべての可能なデータ状態において常に成り立つ制約を指し「現時点のデータでは、たまたまそうなっているだけ (つまり、偶然の一致や偶然依存) 」の関係については、関数従属とは見なしません。

たとえば、上記の例で、レベル51のキャラクタが将来的に複数存在する可能性がある場合、level カラムから、他のカラムへの関数従属は成立しません。関数従属の判断は、ビジネスルールやドメイン知識に基づいた分析 が必要になる点に注意してください。

RDBのテーブルは、「主キー以外のすべてのカラム」が「主キー」に関数従属するように設計することが基本となります。ただし、第2正規形では、さらに 複合主キーの一部に対する 部分関数従属 を排除し、完全関数従属 することが求められます。

(プロンプト例)

RDBにおいて、関数従属であるかは、レコードのサンプルで判断するだけではなく、ドメイン知識に基づいて判断するべきであると説明されました。そもそも「ドメイン知識」とはなんですか。

5.1.1 定着確認

5.2 部分関数従属と完全関数従属

RDB において、テーブルの主キーが複数のカラムから構成される場合 (つまり、複合主キーである場合)、その主キーを構成するカラムの一部だけに従属するカラムが存在することがあります。

たとえば、以下のようなテーブルがあるとします。

character_id name item_id item qty
1 Alice 1 Map 1
1 Alice 2 Potion 5
2 Bob 3 Ring 3
3 Charlie 4 Wand+1 1
3 Charlie 5 Scroll 4
3 Charlie 2 Potion 3
4 Ellen

このテーブルは character_iditem_id で複合主キーを構成していますが、次のように 複合主キーの「一部だけ」に従属するカラム が存在しています。

このような状態を 部分関数従属 (PFD: Partial Functional Dependency) といいます。それに対して、複合主キーのすべてのカラムをそろえてはじめて値が一意に決定する関係完全関数従属 (FFD: Full Functional Dependency) といいます。

たとえば、上記のテーブルで qty カラムは、character_id と item_id の両方がそろわなければ値が一意に決まってきません。このようなとき、qty は character_id と item_id に完全関数従属している といいます。

主キーが1つのカラムで構成される場合 (=複合主キーではない場合)、主キーに「部分」という概念が存在しないため、部分関数従属の関係になるようなカラムは存在しません。ただし、サロゲートキーを設定しているときは、潜在的に部分関数従属が存在している場合があり、第2正規化では、そのようなケースを含めて排除する必要があります。

5.2.1 定着確認

次のテーブルは、book_idauthor_id により、複合主キーが設定されている。以下の各問いに答えよ。

book_id author_id title author author_order
1 1 New Python Alice 1
1 2 New Python Ellen 2
2 3 The React 1 Bob 1
3 3 The React 2 Bob 1
3 1 The React 2 Alice 2
3 4 The React 2 Charlie 3
4 4 PostgreSQL Charlie 1
4 2 PostgreSQL Ellen 2

5.3 第2正規形

第2正規化 では、第1正規形の要件を満たしつつ、複合主キーの一部に対する 部分関数従属 を排除するように (=完全関数従属 になるように) テーブルを分解していきます。

たとえば、次のようなテーブル (=部分関数従属を含むテーブル) があるとき…

▼ 第1正規形 (第2正規形未満)

character_id name item_id item qty
1 Alice 1 Map 1
1 Alice 2 Potion 5
2 Bob 3 Ring 3
3 Charlie 4 Wand+1 1
3 Charlie 5 Scroll 4
3 Charlie 2 Potion 3
4 Ellen

このテーブルを、第2正規形の要件を満たすように3つのテーブルに分解することができます。一部、カラム名を整理しています。

▼ characters テーブル (第2正規形)

character_id name
1 Alice
2 Bob
3 Charlie
4 Ellen

▼ items テーブル (第2正規形)

item_id name
1 Map
2 Potion
3 Ring
4 Wand+1
5 Scroll

▼ character_items テーブル (第2正規形・複合主キー・完全関数従属)

character_id
(FK)
item_id
(FK)
qty
1 1 1
1 2 5
2 3 3
3 4 1
3 5 4
3 2 3

既に気づいたかもしれませんが、この分解操作は、前回講義で学んだ多対多のリレーションの分割 (中間テーブル化)と本質的に同じ考え方です。そのため、論理ER図を適切に設計できていれば、結果として自然に第2正規形となっている場合が少なくありません。

img

5.3.1 定着確認

book_id author_id title author author_order
1 1 New Python Alice 1
1 2 New Python Ellen 2
2 3 The React 1 Bob 1
3 3 The React 2 Bob 1
3 1 The React 2 Alice 2
3 4 The React 2 Charlie 3
4 4 PostgreSQL Charlie 1
4 2 PostgreSQL Ellen 2

6 第3正規化

第3正規化 では、第2正規形の要件を満たしつつ、非キー属性同士推移的関数従属 を排除していきます。非キー属性とは 主キーを構成しないカラム、つまり主キー以外のすべてのカラム を指します。

6.1 推移的関数従属

推移的関数従属 (TFD: Transitive Functional Dependency) とは、主キー以外のカラムに関数従属している関係 を表します。砕けた表現をすれば「主キー以外のカラムが「中継点」のようになって、別の非キーを決めてしまう関係」を推移的関数従属といいます。

つまり、\(\{\) 主キー\(\} \rightarrow \{\) 非キーA \(\} \rightarrow \{\) 非キーB \(\}\) という依存の連鎖があるとき「非キーBは、主キーに推移的に従属している」のように表現します。

たとえば、次のようなテーブルを考えます。このテーブルで「主キー」は 下線 で示すように character_id であり、「非キー」は name、job_id、job_name となります。このテーブルは…

character_id name job_id job_name
1 Alice J5 Priest
2 Bob J2 Monk
3 Charlie J6 Wizard
4 Ellen J6 Wizard

このテーブルで注目すべきは、

という構造になります。つまり、\(\{\) character_id \(\} \rightarrow \{\) job_id \(\} \rightarrow \{\) job_name \(\}\) という関係が成立し、「job_name は、主キーに推移的に従属している」といえます。

このような 推移的関数従属 を排除するようにテーブルを分解する操作が 第3正規化 となります。

6.2 第3正規形

次のような推移的関数従属 (=具体的には \(\{\) character_id \(\} \rightarrow \{\) job_id \(\} \rightarrow \{\) job_name \(\}\)) を含むテーブルは…

character_id name job_id job_name
1 Alice J5 Priest
2 Bob J2 Monk
3 Charlie J6 Wizard
4 Ellen J6 Wizard

以下のような第3正規形の2つのテーブル (charactersjobs) に分解することができます。

▼ characters テーブル (第3正規形)

character_id name job_id (FK)
1 Alice J5
2 Bob J2
3 Charlie J6
4 Ellen J6

▼ jobs テーブル (第3正規形)

job_id name
J1 Fighter
J2 Monk
J3 Ninja
J4 Samurai
J5 Priest
J6 Wizard

これらのテーブルについて「論理ER図」を作成すると次のようになります (characters テーブルの job_id カラムに NOT NULL制約 を想定して作成した図になります)。

img

第3正規形にすることで、いずれのキャラクタも就いていないジョブ情報 (たとえば、Fighter や Ninja、Samurai など) が保持可能な構造になることに注目してください。

6.2.1 定着確認

book_id title primary_author primary_author_tel publisher publisher_tel
1 New Python Alice 5501 Hoge Inc. 4771
2 React 1 Bob 3000 Fuga Inc. 4649
3 React 2 Bob 3000 Fuga Inc. 4649
4 PostgreSQL Charlie 5432 Hoge Inc. 4771
5 Hello COBOL Ellen 8080 Piyo Inc. 2951
6 OpenCV ! Alice 5501 Piyo Inc. 2951

6.3 高次正規化

ここまで、第1正規形第2正規形第3正規形 と順に解説してきました。先にも触れたように、この先には ボイス・コッド正規形 (第3.5正規形)、第4正規形、第5正規形 と続いていきますが、実務でも資格試験でも、第3正規形まで満たせていれば十分なケースがほとんどであり、本講義でもそこまでの解説に留めています。

正規化について、より深く学びたい学生は、📖 教科書「達人に学ぶDB設計 徹底指南書 (第2版)」で丁寧に解説されているので、読んでみてください。

(プロンプト例)

RDB の設計に関する質問です。実務においては第3正規形までの正規化で十分なケースが多いと聞きました。なぜ、第4正規形、第5正規形まで正規化をしないのですか。どのような背景や理由があるのですか。

6.4 論理設計のまとめ

ここまでに解説してきたものは「形式的に正しい構造をつくる」という側面での論理設計でしたが、現実の開発では「どのように アプリ から DB を使うのか」という側面からの設計テクニックや、逆に避けるべきアンチパターンも数多く存在します。これらはビジネスルールやドメイン知識に強く依存するため、授業では踏み込みませんが、Zenn、Qiita、書籍、動画などのナレッジを追いかけておくことで、より現場に近いデータベース設計の感覚を身につけることができるようになります。

以下の動画は、初心者がやらかしがちなデータベース設計のアンチパターンについてクイズ形式で解説しているものになります。ここまでに学んだ知識で十分に理解できる内容となっています。短時間で視聴できる動画なので、ぜひ、視聴してみてください。

また、現場で遭遇する「論理設計のアンチパターン」については📖 教科書「達人に学ぶDB設計 徹底指南書 (第2版)」でも解説されているので、興味のある学生は読んでみてください。上記の動画と同様に、ここまでの授業内容が理解できていれば、概ね内容を理解することができると思います。

7 物理設計 (基礎)

物理設計 (Physical Design) では、PostgreSQL や MySQL などの実際の RDBMS で動作させることを前提に、具体的なテーブルやインデックスの設計を行います。ここには、データ型の選択外部キーやCHECK制約などの設定テーブル定義 (=CREATE TABLE などのDDL の記述)、検索性能を意識したインデックス設計といった作業が含まれます。

また、物理設計には、ストレージ、メモリ、CPU などハードウェア特性を踏まえたストレージ構成、冗長化の仕組み (たとえば RAID構成など)、バックアップやレプリケーション、負荷分散など、ハードウェアおよび運用面を含めた設計も含まれます。

なお、本講義ではまず、これらのうち「テーブル設計」「制約設定」「インデックス設計」など、RDBMS 上で完結する基礎的な物理設計を中心に扱います。RDBMS としては PostgreSQL 17 を想定します。

(プロンプト例)

データベースにおける「レプリケーション」とはどのようなものですか。「バックアップ」とは何が違いますか。

7.0.1 定着確認

7.1 テーブルの定義

RDBMS に「テーブル」を作成するためには、SQL の CREATE TABLE 文を使用します。最小構成の CREATE TABLE には ❶テーブル名 (慣例として複数形の名詞もしくは集合名詞)、❷カラム名、❸データ型、❹主キーの指定といった基本要素を記述します。

たとえば、次のような「論理ER図」で示されたエンティティは…

img

次のような SQL でテーブルを定義/作成できます。

-- 既に存在するなら削除
DROP TABLE IF EXISTS p_characters;

-- テーブルの作成
CREATE TABLE p_characters (
  character_id INTEGER PRIMARY KEY, -- 整数型、PK
  name VARCHAR(16) NOT NULL,        -- 可変長文字列(最大16文字)
  buff DECIMAL(3, 2) NOT NULL,      -- 固定小数点数型 (-9.99〜9.99)
  is_online BOOLEAN NOT NULL,       -- 真偽値
  notes TEXT,                       -- 可変長文字列(実質無制限)
  created_on DATE NOT NULL,         -- 日付型
  last_login_at TIMESTAMP           -- 日時型(タイムゾーンなし)
);

-- レコードの挿入
INSERT INTO
  p_characters (character_id, name, buff, is_online, notes, created_on, last_login_at)
VALUES
  (
    6,
    'Alice',
    0.00,
    TRUE,
    NULL,
    DATE '2021-06-14',
    TIMESTAMP '2025-09-18 14:22:00'
  ),
  (
    8,
    'Bob',
    9.99,
    FALSE,
    'Admin note: Detected abnormal behavior; monitoring enabled.',
    DATE '2022-03-22',
    TIMESTAMP '2025-08-03 09:11:00'
  );

-- レコードの確認
SELECT
  *
FROM
  p_characters;

SQL演習用のプロジェクトに sql/07/tmp.sql などの適当なファイルを作成して、この SQL を貼り付けて、実際に実行してみてください。

次のような実行結果が得られると思います (以下では、notes カラムの表示を省略しています)。

DROP TABLE
CREATE TABLE
INSERT 0 2
 character_id | name  | buff | is_online | notes       | created_on |    last_login_at
--------------+-------+------+-----------+-------------+------------+---------------------
            6 | Alice | 0.00 | t         |             | 2021-06-14 | 2025-09-18 14:22:00
            8 | Bob   | 9.99 | f         | Admin no... | 2022-03-22 | 2025-08-03 09:11:00

第02行目 は「既に p_characters テーブルが存在していれば削除する」という意味の文になります。存在しないテーブルを削除 (DROP) しようとするとエラーになるため、IF EXISTS キーワードを追加しています。

第05行目 から 第13行目CREATE TABLE p_characters (...) がテーブルを新規に作成するための文になります。基本的に カラム名 (Column Name) 👉 データ型 (Data Type) 👉 制約 (Constraint) という順で記述します。

第16行目 から 第36行目 では、作成したテーブルにレコードを挿入しています。第25行目では、DATE '2021-06-14' のように 型付きリテラル によって値を設定していますが、単に '2021-06-14' としても (暗黙に変換されるので) 問題ありません。

7.1.1 演習

7.1.2 定着確認

テーブル users が存在する場合にのみ、そのテーブルを削除し、存在しない場合はエラーにならないようにすること。

7.2 データ型

PostgreSQL で利用可能な 代表的なデータ型 としては、次のようなものがあります。

PostgreSQLで利用可能なデータ型

PostgreSQL は、リレーショナルモデルではあるものの、オブジェクト指向データベース的な機能も有しており、「可変長配列型」や「JSON型」といった構造化データをそのまま扱うカラムを定義することができます。これらの値は文字列としてシリアライズされて保存されるわけではなく、検索や更新を高速化するための構造化された形式で内部的に保持されています。

ただし、本科目ではこれらの機能は扱わず、純粋な RDB としての「基本的なデータ型のみ」を対象とします。

(プロンプト例)

PostgreSQL に関する質問です。データ型の VARCHAR(n) と TEXT は、どのように使い分けたら良いかを実務的な観点から語ってください。

PostgreSQL に関する質問です。データ型として DECIMAL(n, m) ではなく、単に DECIMAL を指定すると、桁数や小数点以下の桁数が固定されない任意精度型になると聞きました。この任意精度型とは、どのようなものか、浮動小数点数型と比較しながら分かりやすく丁寧に解説してください。

7.2.1 定着確認

7.3 非NULL制約 (NOT NULL制約)

レコード内の特定のカラムに対して 「値が入っていない状態」 を許容したくない場合は、CREATE TABLE のなかで「非NULL制約」を指定します。たとえば is_online BOOLEAN NOT NULL のように記述すると、is_online カラムには NULL が格納できなくなります。逆に言えば、NOT NULL を明示しなければ、そのカラムには NULL が格納できることになります。

7.3.1 定着確認

CREATE TABLE users (
  id INTEGER PRIMARY KEY,
  name VARCHAR(16)
);

7.4 PRIMARY KEY指定

CREATE TABLE において、主キーは PRIMARY KEY キーワードを使用して指定します。たとえば、次のようにすることで character_id を主キーに指定することができます。このような指定方法を「カラムレベルの主キー指定」といいます。

CREATE TABLE characters (
  character_id INTEGER PRIMARY KEY,
  name VARCHAR(16) NOT NULL
);

また、次のようにテーブル定義の末尾に主キーの指定を記述することもできます。このような形式を「テーブルレベルの主キー指定」といいます。

CREATE TABLE characters (
  character_id INTEGER,
  name VARCHAR(16) NOT NULL,
  PRIMARY KEY (character_id)
);

複合主キーは、テーブルレベルの主キー指定を用いて次のように記述します。

CREATE TABLE character_items (
  character_id INTEGER,
  item_id INTEGER,
  quantity INTEGER NOT NULL,
  PRIMARY KEY (character_id, item_id)
);

複合主キーに含まれる character_id と item_id には、それぞれ自動的に「非NULL制約」が適用されます。また、character_id と item_id の組み合わせに対して「UNIQUE制約」が自動適用されます。

7.4.1 定着確認

CREATE TABLE character_guilds (
  character_id INTEGER,
  guild_id INTEGER,
  joined_at TIMESTAMP,

);
CREATE TABLE character_items (
  character_id INTEGER,
  item_id      INTEGER,
  quantity     INTEGER NOT NULL,
  PRIMARY KEY (character_id, item_id)
);

7.4.2 SQLドリル💻

-- トランザクションの開始
START TRANSACTION;

-- p_users テーブルの作成 (ここを記述)

-- p_users テーブルにレコードを挿入 (ここを記述)

-- テーブルの確認
SELECT
  *
FROM
  p_users;

-- p_users テーブルを集計 (ここを記述)

-- ロールバックによる処理の取り消し
ROLLBACK;

[期待する実行結果]

START TRANSACTION
CREATE TABLE
INSERT 0 5
 user_id |  name   | is_admin | height_cm |  birthday
---------+---------+----------+-----------+------------
       1 | Alice   | t        |     162.3 | 2003-10-20
       2 | Bob     | f        |     181.9 | 2002-05-05
       3 | Charlie | f        |           |
       4 | Ellen   | t        |     172.7 |
       5 | Eve     | f        |           | 2024-06-30
(5 行)

 user_count | height_avg | admin_count
------------+------------+-------------
          5 |     172.30 |           2
(1 行)

8 授業時間外学習の指示 (宿題)

🚨本科目は「学修単位科目」であり、1回の講義あたり「4時間相当」の授業時間外学習が求められる科目です🏃


9 定着確認の解答例

9.0.1 解答例-1nf-01

book_id author_id title author author_order
1 1 New Python Alice 1
1 2 New Python Ellen 2
2 3 The React 1 Bob 1
3 3 The React 2 Bob 1
3 1 The React 2 Alice 2
3 4 The React 2 Charlie 3
4 4 PostgreSQL Charlie 1
4 2 PostgreSQL Ellen 2

9.0.2 解答例-2nf-01

あくまで設計の「一例」です。

▼ books テーブル

book_id title
1 New Python
2 The React 1
3 The React 2
4 PostgreSQL

▼ authors テーブル

author_id name
1 Alice
2 Ellen
3 Bob
4 Charlie

▼ book_authors テーブル

book_id author_id author_order
1 1 1
1 2 2
2 3 1
3 3 1
3 1 2
3 4 3
4 4 1
4 2 2

9.0.3 解答例-3nf-01

あくまで設計の「一例」です。

▼ authors テーブル

author_id name tel
1 Alice 5501
2 Bob 3000
3 Charlie 5432
4 Ellen 8080

▼ publishers テーブル

publisher_id name tel
1 Hoge Inc. 4771
2 Fuga Inc. 4649
3 Piyo Inc. 2951

▼ books テーブル

book_id title primary_author_id
(FK)
publisher_id
(FK)
1 New Python 1 1
2 React 1 2 2
3 React 2 2 2
4 PostgreSQL 3 1
5 Hello COBOL 4 3
6 OpenCV ! 1 3

(プロンプト例)

RDB のテーブル設計に関する相談です。users というテーブルの主キーとなるカラムの名前を id にすべきか user_id にすべきか悩んでいます。それぞれで一長一短があると思うのですが、そのあたりを教えてください。