EN813304 · Midterm
บทที่ 2 / 12 · intro2rel-model.pptx · สัปดาห์ที่ 2

Relational Model & Keys

relation / tuple / attribute / domain · superkey · candidate key · primary key · foreign key และ referential integrity

ออกสอบบ่อยมากKeys
หัวข้อในบทนี้

วิดีโอที่อาจารย์ให้ดูก่อนเรียนบทนี้:

Introduction to Relational Model

2.1Relational Model คืออะไร

Relational model คือกรอบทฤษฎีสำหรับแทนและจัดการข้อมูล เสนอโดย E.F. Codd ในปี 1970 และเป็นรากฐานของฐานข้อมูลเชิงสัมพันธ์ทุกตัวที่เราใช้ทุกวันนี้

กายวิภาคของ relation

IDnamedept_namesalary
10101SrinivasanComp. Sci.65000
12121WuFinance90000
15151MozartMusic40000
คำศัพท์ความหมาย
Relationตาราง
Tupleแถว 1 แถว = element t ของ relation r
Attributeคอลัมน์
Domainเซตของค่าที่อนุญาตสำหรับ attribute นั้น
Arityจำนวน attribute (จำนวนคอลัมน์)
Cardinalityจำนวน tuple (จำนวนแถว)
Relation schemaR = (A₁, A₂, …, Aₙ) เช่น instructor = (ID, name, dept_name, salary)
Relation instancer(R) = ค่าจริงในตาราง ณ ขณะหนึ่ง

ในนิยามเชิงคณิตศาสตร์: ให้เซต D₁, D₂, …, Dₙ · relation r คือ สับเซตของ D₁ × D₂ × … × Dₙ (เครื่องหมาย × ตรงนี้คือ Cartesian product = การจับคู่ทุกค่าจากทุก domain เข้าด้วยกัน ไม่ใช่การคูณเลข — เรียนเต็ม ๆ ในบทที่ 3) ดังนั้น relation จึงเป็น เซตของ n-tuple (a₁, a₂, …, aₙ) โดยที่ aᵢ ∈ Dᵢ

3 กฎเหล็กของ attribute และ relation 1. ค่าของ attribute ต้องเป็น atomic (แบ่งย่อยไม่ได้) — นี่คือที่มาของ First Normal Form (1NF) = กฎที่บังคับว่าทุกช่องต้องเก็บค่าเดี่ยว แบ่งย่อยไม่ได้ และเป็นเหตุผลที่เก็บเบอร์โทรหลายเบอร์ในช่องเดียวไม่ได้ (ทฤษฎี normalization เต็ม ๆ เรียนหลังมิดเทอม — ก่อนสอบรู้แค่ 1NF พอ)
2. ค่าพิเศษ null เป็นสมาชิกของทุก domain แปลว่า “ไม่ทราบค่า” · null สร้างความยุ่งยากให้นิยามของ operation หลายตัว (ดูบทที่ 7)
3. ลำดับของ tuple ไม่มีความหมาย — relation คือเซต ระบบจะเก็บเรียงยังไงก็ได้ และห้ามมี tuple สองแถวที่ค่าเหมือนกันทุก attribute

2.2Keys — หัวใจของบทนี้

เราต้องมีวิธีระบุว่า tuple แต่ละแถวใน relation ต่างกันอย่างไร ซึ่งแสดงออกผ่านค่าของ attribute ค่าเหล่านั้นต้องระบุ tuple ได้เฉพาะตัว และตามกฎข้อ 3 ด้านบน จะไม่มี tuple สองแถวที่มีค่าเหมือนกันทั้งหมด

Superkey

Superkey คือ เซตของ attribute ตั้งแต่หนึ่งตัวขึ้นไปที่เมื่อรวมกันแล้วสามารถระบุ tuple ได้เฉพาะตัว

นิยามเชิงรูปนัย ให้ R เป็นเซตของ attribute ใน schema ของ relation r
สับเซต K ⊆ R เป็น superkey ของ r ก็ต่อเมื่อ: ถ้า t₁ และ t₂ อยู่ใน r และ t₁ ≠ t₂ แล้ว t₁.K ≠ t₂.K
พูดง่าย ๆ: ไม่มี tuple สองแถวที่มีค่าเหมือนกันบน attribute ทุกตัวใน K

ตัวอย่าง: ID เพียงตัวเดียวก็แยกอาจารย์แต่ละคนได้แล้ว → ID เป็น superkey · ส่วน name ไม่ใช่ superkey เพราะอาจารย์หลายคนอาจชื่อเหมือนกัน

Candidate key

superkey อาจมี attribute ส่วนเกิน ติดมาด้วย เช่น {ID, name} ก็เป็น superkey ของ instructor เพราะถ้า K เป็น superkey แล้ว ทุก superset ของ K ก็เป็น superkey ด้วย

Candidate key = superkey ที่ minimal คือ superkey ที่ไม่มีสับเซตแท้ตัวใดเป็น superkey ได้อีก

กับดักที่ออกสอบบ่อยที่สุดในบทนี้ สมมติว่า {name, dept_name} รวมกันแยกอาจารย์ได้ ดังนั้น ทั้ง {ID} และ {name, dept_name} เป็น candidate key

แต่ {ID, name} ไม่ใช่ candidate key ถึงแม้จะแยก tuple ได้ก็ตาม — เพราะ ID เพียงตัวเดียวก็เป็น candidate key อยู่แล้ว จึงตัด name ทิ้งได้ → ไม่ minimal

Primary key

Primary key คือ candidate key ที่ผู้ออกแบบฐานข้อมูลเลือก ให้เป็นวิธีหลักในการระบุ tuple

ตัวอย่างจากสไลด์

ระบบลงทะเบียนนักศึกษา

Students(StudentID, FirstName, LastName, DateOfBirth, Email, PhoneNumber)

Superkeys: {StudentID}, {Email}, {StudentID, FirstName}, {StudentID, Email}, … ชุดใดก็ตามที่มี StudentID หรือ Email อยู่
Candidate keys: {StudentID}, {Email}
Primary key: StudentID

เหตุผล: StudentID ระบบออกให้และรับประกันว่าไม่ซ้ำ · Email เป็น candidate key ได้ แต่นักศึกษาเปลี่ยนอีเมลได้

ระบบทรัพยากรบุคคล

Employees(EmployeeID, SocialSecurityNumber, FirstName, LastName, Department, Position, HireDate)

Candidate keys: {EmployeeID}, {SocialSecurityNumber}
Primary key: EmployeeID

เหตุผล: ทั้งคู่ระบุพนักงานได้เฉพาะตัว แต่เลือก EmployeeID เพราะระบบสร้างให้เอง และการใช้เลขประกันสังคมมีประเด็นเรื่องความเป็นส่วนตัว

2.3Foreign Key และ Referential Integrity

relation r₁ อาจมี attribute ที่เป็น primary key ของอีก relation หนึ่ง คือ r₂ attribute นั้นเรียกว่า foreign key จาก r₁ อ้างอิงไปยัง r₂

ตัวอย่างในสไลด์: attribute dept_name ใน instructor เป็น foreign key อ้างอิง department เพราะ dept_name เป็น primary key ของ department

department (referenced)
dept_name PKbuildingbudget
Comp. Sci.Taylor100000
FinancePainter120000
MusicPackard80000
instructor (referencing)
ID PKnamedept_name FK
10101SrinivasanComp. Sci.
12121WuFinance
15151MozartMusic
Foreign key constraint ทำอะไร 1. ป้องกันการกระทำที่จะทำลายลิงก์ระหว่างตาราง (เช่น ลบแผนกที่ยังมีอาจารย์สังกัดอยู่)
2. ป้องกันการใส่ข้อมูลที่ไม่ถูกต้องลงในคอลัมน์ FK เพราะค่าต้องเป็นหนึ่งในค่าที่มีอยู่ในตารางที่มันชี้ไป

ตัวอย่างต่อไปใช้ตาราง Enrollment ซึ่งมีหน้าตาแบบนี้ (จะอธิบายเรื่อง composite key ในหัวข้อ 2.4):

CREATE TABLE Enrollment (
    student_id  CHAR(10)  NOT NULL,
    course_id   CHAR(8)   NOT NULL,
    semester    CHAR(1)   NOT NULL,
    year        YEAR      NOT NULL,
    grade       CHAR(2),
    PRIMARY KEY (student_id, course_id, semester, year),
    FOREIGN KEY (student_id) REFERENCES Student(student_id),
    FOREIGN KEY (course_id)  REFERENCES Course(course_id)
);
-- ค่าที่ใส่ใน INSERT เรียงตามลำดับคอลัมน์นี้:
--   student_id, course_id, semester, year, grade

ก่อนจะทดลอง insert ต้องรู้ก่อนว่าตารางแม่ทั้งสองมีแถวอะไรอยู่บ้าง เพราะ FK จะเช็คกับแถวเหล่านี้:

Student
student_idname
6401001001Anan
6401001002Beam
6401001003Chada

ไม่มี 9999999999

Course
course_idtitle
EN813304Database Systems
EN811001Programming I
ทดลองในคาบ — จะเกิดอะไรขึ้น
-- ผิด: student_id 9999999999 ไม่มีอยู่ในตาราง Student
INSERT INTO Enrollment VALUES ('9999999999', 'EN813304', '1', 2026, 'A');
-- ERROR 1452: Cannot add or update a child row:
--             a foreign key constraint fails

-- ถูก: student_id 6401001001 มีอยู่จริง → ผ่าน constraint
INSERT INTO Enrollment VALUES ('6401001001', 'EN813304', '1', 2026, 'B');
-- (แต่ถ้าคีย์ชุดนี้ซ้ำกับแถวเดิม จะติด PRIMARY KEY แทน)
โจทย์จากแบบฝึกหัดในคาบ: INSERT ไหนสำเร็จ ไหนล้มเหลว กำหนดสองตารางนี้:
CREATE TABLE Departments (
    dept_id   INT PRIMARY KEY,
    dept_name VARCHAR(50) NOT NULL
);
CREATE TABLE Employees (
    emp_id   INT PRIMARY KEY,
    emp_name VARCHAR(50) NOT NULL,
    dept_id  INT,
    FOREIGN KEY (dept_id) REFERENCES Departments(dept_id)
);
-- ใน Departments มีอยู่แค่ 2 แถว:  (1, 'HR')  และ  (2, 'IT')
a) INSERT INTO Employees VALUES (101,'John Doe', 2); → สำเร็จ เพราะ dept_id = 2 มีอยู่จริงใน Departments
b) INSERT INTO Employees VALUES (102,'Jane Smith', 4); → ล้มเหลว เพราะ dept_id = 4 ไม่มีใน Departments ทำให้ผิด foreign key constraint

กรณีที่ประกาศ foreign key ไม่ได้ — ตัวอย่าง section / teaches

ก่อนอื่นต้องรู้จักสองตารางนี้ก่อน (จะใช้ต่อในบทที่ 3 และ 9 ด้วย):

section — “การเปิดสอนวิชาหนึ่งครั้ง”
course_id PKsec_id PKsemester PKyear PK
CS-1011Fall2017
CS-1012Fall2017
CS-3471Fall2017

ต้องใช้ครบ 4 คอลัมน์ถึงระบุได้ว่าเป็นการเปิดสอนครั้งไหน

teaches — “ใครสอน section ไหน”
IDcourse_idsec_idsemesteryear
10101CS-1011Fall2017
45565CS-1011Fall2017
83821CS-3471Fall2017

สองแถวบนมี (course_id, sec_id, semester, year) ซ้ำกัน เพราะอาจารย์สองคนสอน section เดียวกัน

ทีนี้สมมติเราอยากบังคับว่า “ถ้ามี section ของรายวิชาหนึ่งอยู่ ต้องมีอาจารย์สอนอย่างน้อยหนึ่งคน” แต่ก็เป็นไปได้ที่จะมีอาจารย์สอนมากกว่าหนึ่งคน

เพื่อบังคับข้อนี้ เราจะต้องกำหนดว่า ถ้าชุดค่า (course_id, sec_id, semester, year) ปรากฏใน section ชุดเดียวกันนั้นต้องปรากฏใน teaches ด้วย แต่ชุดค่านี้ไม่ได้เป็น primary key ของ teaches (เพราะอาจารย์หลายคนสอน section เดียวกันได้ ค่าจึงซ้ำได้)

สรุปประเด็นนี้ เราประกาศ foreign key จาก section ไป teaches ไม่ได้ — เพราะ FK ต้องชี้ไปยังคีย์ที่ไม่ซ้ำ ของตารางปลายทาง
แต่เราประกาศในทิศทางกลับกันได้ คือจาก teaches → section เพราะ (course_id, sec_id, semester, year) เป็น PK ของ section

ตัวอย่างชุด FK จากสไลด์

ระบบสั่งซื้อร้านหนังสือออนไลน์
Customers(CustomerID, FirstName, LastName, Email, Address)
Books(BookID, Title, Author, ISBN, Price)
Orders(OrderID, CustomerID, OrderDate, TotalAmount)
OrderItems(OrderItemID, OrderID, BookID, Quantity, Price)

FK เหล่านี้รับประกันว่า ทุกออเดอร์ผูกกับลูกค้าที่มีอยู่จริง · ทุกรายการในออเดอร์อยู่ใต้ออเดอร์ที่มีจริง · และอ้างถึงหนังสือที่มีจริง

ระบบจัดการผู้ป่วยในโรงพยาบาล
Insurance(InsuranceID, ProviderName, PolicyNumber)
Patients(PatientID, FirstName, LastName, DOB, InsuranceID)
Doctors(DoctorID, FirstName, LastName, Specialization)
Appointments(AppointmentID, PatientID, DoctorID,
             AppointmentDate, AppointmentTime)

FK รับประกันว่าผู้ป่วยทุกคนผูกกับกรมธรรม์ที่มีอยู่จริง และทุกนัดหมายเชื่อมกับทั้งผู้ป่วยและแพทย์ที่มีจริง

2.4Composite Primary Key

บางตารางต้องใช้หลาย attribute รวมกันจึงจะระบุแถวได้เฉพาะตัว — จากแบบฝึกหัดในคาบ:

exploring-relational-model — ตาราง Enrollment
CREATE TABLE Enrollment (
    student_id  CHAR(10)  NOT NULL,
    course_id   CHAR(8)   NOT NULL,
    semester    CHAR(1)   NOT NULL,
    year        YEAR      NOT NULL,
    grade       CHAR(2),
    PRIMARY KEY (student_id, course_id, semester, year),
    FOREIGN KEY (student_id) REFERENCES Student(student_id),
    FOREIGN KEY (course_id)  REFERENCES Course(course_id)
);
ทำไม student_id เพียงตัวเดียวเป็น PK ของ Enrollment ไม่ได้

เพราะนักศึกษาหนึ่งคนลงทะเบียนได้หลายวิชา ในข้อมูลจริงมีแถว ('6401001001','EN813304',…) และ ('6401001001','EN813201',…) ถ้าใช้ student_id เป็น PK จะเก็บได้แค่แถวเดียว ระบบจะปฏิเสธแถวที่สอง

ยิ่งกว่านั้น นักศึกษาคนเดิมลงวิชาเดิมซ้ำได้ในภาคเรียนหรือปีต่างกัน (เช่น ตก แล้วลงใหม่) จึงต้องใส่ semester และ year เข้าไปใน PK ด้วย

(student_id, course_id, semester, year, grade) เป็น superkey ไหม เป็น candidate key ไหม

เป็น superkey — ใช่ เพราะมันมี PK อยู่ครบ จึงระบุ tuple ได้เฉพาะตัวแน่นอน (superset ของ superkey ก็ยังเป็น superkey)

เป็น candidate key หรือไม่ — ไม่ใช่ เพราะตัด grade ออกได้ แล้วยังระบุ tuple ได้เฉพาะตัวอยู่ จึงไม่ minimal

นี่คือความต่างระหว่าง superkey กับ candidate key ที่ข้อสอบชอบถาม

grade = NULL แปลว่าได้เกรด 0 หรือเปล่า

ไม่ใช่ · NULL แปลว่า “ไม่ทราบค่า” หรือ “ยังไม่มีค่า” ไม่ใช่ศูนย์และไม่ใช่สตริงว่าง

ในบริบทนี้แปลว่า ยังไม่ได้ตัดเกรด — นักศึกษาลงทะเบียนแล้วแต่ภาคเรียนยังไม่จบ หรืออาจารย์ยังไม่ส่งเกรด

ถ้าเก็บเป็น 0 จะผิดความหมาย — “ยังไม่ตัดเกรด” กับ “ได้ 0” เป็นคนละเรื่อง
และถ้าคอลัมน์เกรดเป็นตัวเลข (เช่น grade_point DECIMAL(3,2) แทน grade CHAR(2) ในตัวอย่างนี้) การเก็บ 0 แทน NULL จะทำให้ AVG(grade_point) เพี้ยนทันที เพราะ 0 ถูกนับเข้าไปหาร ขณะที่ NULL จะถูกข้าม (ดูบทที่ 7 เรื่อง aggregate กับ NULL)

2.5Schema Diagram

Schema diagram คือภาพแสดง schema ของฐานข้อมูล ที่บอกว่ามี relation อะไรบ้าง แต่ละ relation มี attribute อะไร อะไรเป็น primary key และ foreign key เชื่อมไปที่ไหน

department dept_name building budget instructor ID name dept_name salary course course_id title dept_name credits section course_id sec_id semester year takes ID course_id … grade FK FK FK FK

สีเหลือง ขีดเส้นใต้ = primary key · สีน้ำเงิน = foreign key · ลูกศรชี้ไปยัง relation ที่ถูกอ้างถึง
คอลัมน์หนึ่งเป็นได้ทั้ง PK และ FK พร้อมกัน — เช่น section.course_id เป็นส่วนหนึ่งของ PK ของ section และเป็น FK ชี้ไป course ด้วย · เช่นเดียวกับ 4 คอลัมน์แรกของ takes ที่เป็นทั้ง PK และ FK ชี้ไป section

2.6Relational Query Language

Relational query language คือภาษาระดับสูงที่ใช้โต้ตอบกับฐานข้อมูลเชิงสัมพันธ์ ให้เรานิยาม จัดการ และดึงข้อมูลจาก relation ได้


2.7สรุปบทนี้

คำนิยามที่ต้องเขียนตอบได้
Schema ของ relationการออกแบบเชิงตรรกะของ relation
Instance ของ relationเนื้อหาของ relation ณ จุดเวลาหนึ่ง
Superkeyเซตของ attribute ตั้งแต่หนึ่งตัวขึ้นไป ที่รับประกันว่าระบุ tuple ได้เฉพาะตัว
Candidate keysuperkey ที่ minimal — เป็น superkey แต่ไม่มีสับเซตใดของมันเป็น superkey
Primary keycandidate key ตัวหนึ่งที่ถูกเลือกมาใช้เป็นหลัก
Foreign keyเซตของ attribute ใน referencing relation ที่ค่าของมันต้องปรากฏเป็นค่า primary key ของ tuple ใด tuple หนึ่งใน referenced relation
Schema diagramภาพแสดง relation ทั้งหมด attribute ของแต่ละตัว พร้อม PK และ FK
กำหนด Student(student_id, national_id, email, full_name) โดย student_id, national_id และ email ล้วนไม่ซ้ำ
ข้อใดเป็น superkey แต่ไม่ใช่ candidate key
{student_id, national_id} ระบุ tuple ได้เฉพาะตัว → เป็น superkey · แต่ตัด national_id ทิ้งแล้วยังระบุได้อยู่ → ไม่ minimal จึงไม่ใช่ candidate key
{email} และ {national_id} ต่างก็ minimal → เป็น candidate key · {full_name} ชื่อซ้ำได้ → ไม่ใช่ superkey ด้วยซ้ำ
ข้อความใดผิดเกี่ยวกับ relational model
relation หนึ่งมี candidate key ได้หลายตัว (เช่น student_id, national_id, email) แต่เลือกมาเป็น primary key ได้แค่ตัวเดียว — อย่าสับสนสองอย่างนี้
ระบบจำไว้ในเบราว์เซอร์นี้ จะได้รู้ว่าอ่านถึงไหนแล้ว