National Central University
Uedu Main Site
Explore Uedu
Student Console
Register as Member/Login
Research Informed Consent Center
Survey Center
Educational Big Data Challenge
Claim or create a course
Try an AI Scenario Task
Support & Messages
Uptime Data

UeduGPTs

--

Jupyters

3

Local AI

--

Uedu Code

--

中央大學 AQI 78 24°C PM2.5 18
AI Reply Desktop Notifications

Show a desktop notification when the AI TA finishes replying

Chat Message Notifications

Notify me when classmates post messages in the forum

Sound notification

Play an alert sound whenever there is a new notification

Unit 27

資料庫概念與資料模型

為什麼需要資料庫:資料表、鍵與正規化
Interaction 25 minute(s)

Learning Objectives

  • 說明為什麼大量、多人共用的資料要交給資料庫管理系統,而不是一份份的試算表
  • 認識關聯式資料庫的資料表、列、欄,以及主鍵與外鍵的作用
  • 從一張扁平的表格找出更新異常、插入異常與刪除異常
  • 依「哪個欄位決定哪個欄位」把表格拆開(正規化),並用 JOIN 還原
  • 用實體、屬性與關係(ER 模型)描述資料,並知道 NoSQL 資料庫的幾種類型

選課資料可以只放在一份試算表裡嗎

想像學校的選課資料全部放在一份試算表:每一列是一筆選課,寫著學號、姓名、系所、課號、課名、教師、學分和成績。 幾十筆資料時這樣很方便;但一所大學有上萬名學生、幾千門課,而且教務處、各系辦、授課老師和學生同時在查詢與修改,問題就來了:

  • 兩個人同時改同一份檔案,誰的版本算數?
  • 學生換了系,要改的是一列還是幾十列?漏改了怎麼辦?
  • 老師只能看自己課的成績,學生只能看自己的,這要怎麼控管?
  • 停電或當機的那一刻,正在存檔的資料會不會只寫了一半?

資料庫(database)是一群有組織、彼此相關、可以長期保存的資料;負責管理它的軟體叫資料庫管理系統(DBMS,database management system), 例如 MySQL、PostgreSQL、Oracle Database、Microsoft SQL Server、SQLite。DBMS 幫你處理:

功能 意思
定義資料的結構 事先規定有哪些資料表、每個欄位是什麼型態(例如學分一定是整數)
保持資料一致 用主鍵、外鍵等規則,擋下互相矛盾或指向不存在資料的修改
多人同時存取 很多人同時讀寫時,不會互相蓋掉對方的修改
權限控管 誰可以看、誰可以改,可以設定到資料表甚至欄位
交易與復原 一組修改要嘛全部完成、要嘛全部不算;當機後能回到一致的狀態
快速查詢 從上百萬筆資料裡找出符合條件的幾筆

這一課要回答的問題是:資料要怎麼組織,才不會「改了這裡、忘了那裡」?

關聯式資料庫:資料表、列與欄

現在最常見的是關聯式資料庫(relational database)。它的基礎是 IBM 研究員柯德(E. F. Codd) 1970 年在《Communications of the ACM》發表的論文〈A Relational Model of Data for Large Shared Data Banks〉: 把資料看成一張張的表,用表裡的值把不同的表連起來,使用者只要說「要什麼資料」,不必知道資料在磁碟上怎麼擺放。

名詞 也叫做 意思
資料表(table) 關聯(relation) 描述一種東西的一張表,例如「學生」
列(row) 紀錄(record)、值組(tuple) 一筆資料,例如一位學生
欄(column) 欄位(field)、屬性(attribute) 每筆資料都有的一項性質,例如姓名
students(學生)
+------------+--------+--------+
| student_id | name   | dept   |     ← 欄:每一位學生都有學號、姓名、系所
+------------+--------+--------+
| S001       | 王小明 | 資管系 |     ← 列:一位學生
| S002       | 李佳穎 | 中文系 |
+------------+--------+--------+

兩個最重要的「鍵」(key):

  • 主鍵(primary key,PK):能唯一認出每一列的欄位(或幾個欄位的組合),不能重複、不能空白。 學生的主鍵用學號,不用姓名,因為同名同姓的人很常見。
  • 外鍵(foreign key,FK):這張表裡、用來指向另一張表主鍵的欄位。 例如「選課」表的學號是外鍵,指向「學生」表的學號;資料庫會擋下「學號 S999 選了 CS101」這種指向不存在學生的資料。

主鍵讓每一筆資料有身分證,外鍵讓資料表之間有連結。本課後面的練習都用這兩個概念。

互動練習:一張表存太多事會出什麼問題

下面是一張把學生、課程與成績放在一起的「選課紀錄」,主鍵是(學號, 課號)。依任務操作:用「系所」欄的下拉選單只改王小明其中一列的系所;回答要改幾列才會一致;用「新增一列」試著放進一門還沒有人選的課(學號留白);最後刪掉陳大華選 CS250 的那一列。每一步都看看表格下方紅色的訊息,以及「重複儲存的格數」。隨時可以按「還原成原本的表格」重來。

三種異常,同一個原因

剛才看到的三個問題有正式的名稱:

異常 發生什麼事 例子
更新異常(update anomaly) 同一件事存了好幾份,只改到其中幾份,資料就互相矛盾 王小明的系所存在 3 列,只改 1 列
插入異常(insertion anomaly) 想記錄一件事,卻因為缺了另一件事而放不進去 新課還沒有人選,就沒有學號可填
刪除異常(deletion anomaly) 刪掉一件事,順便把另一件事的唯一紀錄也刪掉了 唯一修 CS250 的人退選,課名和教師也消失了

原因都一樣:一張表描述了好幾件不同的事。「選課紀錄」同時描述了學生(姓名、系所)、課程(課名、教師、學分)和選課(成績), 所以學生和課程的資料,會隨著他們出現在幾筆選課裡而被複製好幾份。

解決的方法是把表格拆開:一件事一張表,表與表之間用鍵連起來。這個過程叫正規化(normalization)。

想一想:主鍵與外鍵

1.

要替「學生」表選主鍵,下面哪一個欄位最適合?

2.

「選課紀錄」的主鍵為什麼是(學號, 課號)兩個欄位的組合,而不是只用學號?

3.

「選課」表的學號是外鍵,參照「學生」表的學號。如果有人想新增一筆「S999 選了 CS101」,但學生表裡沒有 S999,資料庫會怎麼做?

第 1 次作答

正規化:誰決定誰

拆表的依據是函數相依(functional dependency):如果知道 A 的值,就一定知道 B 的值,就說「A 決定 B」,寫成 A → B。

相依 白話
學號 → 姓名、系所 知道學號,就知道是誰、哪一系
課號 → 課名、教師、學分 知道課號,就知道是哪門課
(學號, 課號)→ 成績 要同時知道誰和哪門課,才知道成績

常見的正規化步驟(正規形式,normal form):

  1. 第一正規化(1NF):每一格只放一個值。不要在一格裡寫「CS101、MA101、EN201」,要拆成三列。
  2. 第二正規化(2NF):主鍵是好幾個欄位的組合時,其他欄位要由整個主鍵決定。 課名只由課號決定(只用到主鍵的一部分),所以課名不該留在(學號, 課號)為主鍵的表裡。
  3. 第三正規化(3NF):其他欄位只能由主鍵決定,不能「主鍵決定 B、B 再決定 C」。 例如學生表裡如果放了「系所」和「系辦電話」,系辦電話其實由系所決定,應該另外放到「系所」表。

做完之後,選課紀錄會拆成三張表:

學生(學號 PK, 姓名, 系所)
課程(課號 PK, 課名, 教師, 學分)
選課(學號 PK FK→學生, 課號 PK FK→課程, 成績)

要找回原本的那張大表,就把三張表依共用的欄位合併(JOIN)起來:學號相同的接在一起、課號相同的接在一起。 下一個練習請你親手拆一次,再讓程式做 JOIN,檢查有沒有遺失或多出資料。

互動練習:把選課紀錄拆成三張表

一開始所有欄位都在「選課」表。步驟 1 的表格用勾選決定每個欄位放進哪一張表(共用的欄位可以同時放進兩張);步驟 2 在每張表的卡片勾選主鍵、用下拉選單設定外鍵。檢查結果會即時告訴你主鍵有沒有重複、還有幾格資料重複儲存。黃色的格子是重複儲存的資料。照任務先建學生表、再建課程表,設好外鍵後按「用 JOIN 還原原表」,最後試試在新設計裡新增一門還沒有人選的課。

拆表的代價

拆表之後,每一份資料只存一次:王小明換系只要改學生表的一格,新課可以直接放進課程表,退選也不會讓課程消失。 練習裡「總共存了幾格」也變少了。

代價是查詢時要合併:想看「王小明修了哪些課」,要把學生、選課、課程三張表 JOIN 起來。 資料庫很擅長做這件事,所以一般的系統會先正規化;只有在查詢量極大、JOIN 成為瓶頸時,才會刻意保留一些重複的資料來換取速度, 這叫反正規化(denormalization),而且要另外想辦法保持重複的資料一致。

ER 模型:先畫出有哪些東西、彼此什麼關係

設計資料庫時,通常不是直接開表格,而是先畫實體關係模型(ER model,entity-relationship model)。 它由陳品山(Peter Chen)1976 年在《ACM Transactions on Database Systems》提出,有三個基本元素:

元素 意思 例子 之後變成
實體(entity) 要記錄的一種東西 學生、課程 一張資料表
屬性(attribute) 實體的性質 學號、姓名、課名 欄位
關係(relationship) 實體之間的關聯 學生「選修」課程 外鍵,或一張中間的表

關係還要標出對應數(cardinality),也就是一邊的一筆資料可以對到另一邊幾筆:

系所 ──1──────N── 學生        一個系有很多學生,一位學生屬於一個系(一對多)
學生 ──M──<選修>──N── 課程     一位學生選很多課,一門課有很多學生(多對多)
  • 一對多(1:N):在「多」的那一邊放外鍵,例如學生表放「系所代碼」指向系所表。
  • 多對多(M:N):兩邊都不能只放一個外鍵,要另外建一張中間表,就是剛才的「選課」表; 關係本身的屬性(成績)也放在中間表。
  • 一對一(1:1):例如學生與學生證,通常放在同一張表,或在其中一邊放外鍵。

不是所有資料都適合放進表格:NoSQL

關聯式資料庫要求事先定好欄位,資料一筆一筆整齊地放進表格。有些資料不太適合這樣放: 每筆欄位都不一樣的商品資訊、網站上大量的瀏覽紀錄、社群網站「誰追蹤誰」的關係網。這類需求催生了 NoSQL 資料庫 (通常解讀為 not only SQL:不只是關聯式的表格)。常見的類型:

類型 怎麼存 適合 例子
鍵值(key-value) 一個鍵對到一個值,像 Python 的字典 快取、登入狀態 Redis
文件(document) 一筆資料是一份 JSON 文件,欄位可以不同 商品型錄、內容管理 MongoDB
寬欄(wide-column) 每一列可以有很多、不固定的欄 大量的時間序列與紀錄 Apache Cassandra
圖形(graph) 節點與邊 社群關係、推薦 Neo4j

同一位學生存成文件,可能長這樣:

{ "student_id": "S001", "name": "王小明", "dept": "資管系",
  "courses": [ {"course_id": "CS101", "score": 88}, {"course_id": "MA101", "score": 72} ] }

讀一位學生的全部資料只要拿一份文件,不必 JOIN;但「CS101 這門課有哪些人」就要翻遍所有文件,課名若也存在每份文件裡,又會回到更新異常的問題。 所以 NoSQL 不是關聯式資料庫的升級版,而是不同的取捨:資料長什麼樣子、最常怎麼查,決定該用哪一種。很多系統兩種都用。

想一想:正規化與資料模型

1.

在主鍵為(學號, 課號)的「選課」表裡,哪一個欄位留在這張表是合理的?

2.

學生與社團是多對多的關係(一位學生參加很多社團,一個社團有很多學生)。在關聯式資料庫裡通常怎麼表示?

3.

一個網站要存「誰追蹤誰」,最常做的查詢是「我追蹤的人又追蹤了誰」。哪一種 NoSQL 資料庫的資料模型最貼近這個需求?

第 1 次作答

重點整理

  • 資料庫管理系統負責定義資料結構、保持一致、多人同時存取、權限控管、交易與快速查詢
  • 關聯式資料庫(柯德,1970)把資料放在資料表裡:每一列是一筆資料,每一欄是一項性質
  • 主鍵唯一認出每一列,不能重複、不能空白;外鍵指向另一張表的主鍵,資料庫會擋下指向不存在資料的修改
  • 一張表描述好幾件事,就會出現更新異常、插入異常、刪除異常
  • 正規化依函數相依把表格拆開,一件事一張表:1NF 一格一個值、2NF 由整個主鍵決定、3NF 不能經由別的欄位間接決定;拆開後用 JOIN 還原
  • ER 模型用實體、屬性、關係描述資料;多對多的關係要用一張中間表
  • NoSQL(鍵值、文件、寬欄、圖形)是依資料形狀與查詢方式做的不同取捨,不是關聯式資料庫的替代品

下一課:用 SQL 對這幾張表下指令,查詢、篩選、排序、統計、合併資料表,以及新增、修改、刪除資料。

這一課完成了!
Next lesson:關聯式資料庫與 SQL
你目前沒有登入,學習進度只存在這個瀏覽器。 Login 後,進度會跟著帳號,換電腦也看得到。