Guidelines 4 ข้อของตารางที่ดี
- ความหมายชัด หนึ่งแถวแทน 1 entity หรือ 1 เหตุการณ์ ไม่เอาหลาย entity มาปนในตารางเดียว และอ้างถึงกันด้วย FK เท่านั้น
- ไม่มี anomaly ทั้ง insert, delete และ update
- NULL น้อยที่สุด NULL เกิดได้ 3 แบบคือ not applicable, unknown และ unavailable ถ้าคอลัมน์ไหน NULL เยอะให้แยกออกเป็นตารางใหม่
- ไม่มี spurious tuples แยกตารางแล้วต้อง join กลับได้ข้อมูลเดิม (lossless) ห้ามเสียคุณสมบัตินี้ ส่วน dependency preservation ยอมเสียได้บางกรณี
ตัวอย่างตารางที่ไม่ดี: ใส่ Phone No หลายเบอร์ในช่องเดียว และเอาชื่อแผนกมาปนกับข้อมูลพนักงาน
Anomalies จุดสอบ
EMP_PROJ(Ssn, Pnumber, Hours, Ename, Pname, Plocation)
| ชนิด | เกิดอะไรขึ้น |
| Update | เปลี่ยนชื่อโปรเจกต์ 2 ต้องแก้ทุกแถว ถ้าแก้ไม่ครบข้อมูลจะขัดกัน |
| Insertion | เพิ่มโปรเจกต์ใหม่ไม่ได้ ถ้ายังไม่มีพนักงานทำโปรเจกต์นั้น |
| Deletion | ลบพนักงานคนสุดท้ายของโปรเจกต์ ข้อมูลโปรเจกต์หายไปด้วย |
Anomaly ไม่ได้เกิดจาก SQL แต่เกิดจากการออกแบบตาราง
| Ssn | Pno | Ename | Pname | Ploc |
| 123456789 | 1 | Smith | ProductX | Bellaire |
| 123456789 | 2 | Smith | ProductY | Sugarland |
| 453453453 | 1 | English | ProductX | Bellaire |
ช่องที่ไฮไลต์คือข้อมูลที่ซ้ำ
Functional Dependency
X → Y อ่านว่า "X กำหนด Y" ถ้า t1[X] = t2[X] แล้วต้องได้ t1[Y] = t2[Y] เสมอ FD มาจาก business rule ของโลกจริง ไม่ใช่ดูจากข้อมูลไม่กี่แถว และถ้า K เป็น key แล้ว K → ทุก attribute
Armstrong's Axioms
| Reflexivity | Y ⊆ X ⇒ X→Y |
| Augmentation | X→Y ⇒ XZ→YZ |
| Transitivity | X→Y, Y→Z ⇒ X→Z |
กฎเสริม
| Decomposition | X→YZ ⇒ X→Y, X→Z |
| Union | X→Y, X→Z ⇒ X→YZ |
| Pseudotransitivity | X→Y, WY→Z ⇒ WX→Z |
F⁺ = FD ทุกตัวที่อนุมานได้จาก F ส่วน X⁺ = attribute ทุกตัวที่ X กำหนดได้ ถ้า X⁺ ครอบคลุมทุกคอลัมน์ X เป็น superkey ถ้าตัดอะไรออกไม่ได้อีกแล้ว X คือ candidate key
Minimal cover: RHS มีตัวเดียว, ไม่มี FD ซ้ำซ้อน, LHS ไม่มีตัวเกิน
ลองหา Closure แบบฝึก 2
R(A,B,C,D,E), F = {A→B, B→C, AC→D, D→E}
เริ่มที่ A⁺ = {A}
A→B ได้ {A,B}
B→C ได้ {A,B,C}
มี A และ C แล้ว AC→D ได้ {A,B,C,D}
D→E ได้ {A,B,C,D,E} ครบทุกตัว
A เป็น superkey และเป็น candidate key ส่วน AC ก็เป็น superkey แต่ไม่ใช่ candidate key เพราะ C เกินมา
Keys และ Prime attribute
Superkeyชุดที่มี candidate key อยู่ข้างใน อาจมีตัวเกิน
Candidate keyชุด เล็กที่สุด ที่ระบุแถวได้ไม่ซ้ำ
Primary keycandidate key ที่เลือกมาเป็นหลัก
Alternate keycandidate key ตัวอื่นที่ไม่ได้ถูกเลือก
Prime attributeattribute ที่อยู่ใน candidate key ใดก็ได้
Non-primeattribute ที่ไม่อยู่ใน candidate key ใดเลย
บันได Normal Form จุดสอบ
1NFทุกช่องเป็นค่า atomic ไม่มี multivalued, composite หรือ nested
มีหลายค่าในช่องเดียวไหม?
2NF1NF + ไม่มี partial dependency คือ non-prime ต้องขึ้นกับ key ทั้งชุด
PK เป็น composite ไหม แล้วมีคอลัมน์ที่ขึ้นกับแค่บางส่วนของ PK ไหม?
3NF2NF + ไม่มี transitive dependency คือ non-prime ห้ามกำหนด non-prime
ทุก X→A มี X เป็น superkey หรือ A เป็น prime
BCNFทุก determinant ต้องเป็น superkey (เข้มกว่า 3NF)
LHS ของทุก FD เป็น superkey ไหม?
4NFไม่มี MVD (X →→ Y) ที่ X ไม่ใช่ superkey
มีข้อมูลหลายชุดที่อิสระกันแต่เก็บรวมกันไหม?
5NFJoin dependency หรือ PJNF แยกเป็นตั้งแต่ 3 ตารางแล้ว join กลับได้ถูก
เช่น Supplier–Part–Project
งานจริงทำถึง 3NF หรือ BCNF ก็พอ ส่วน Denormalization คือการรวมตารางกลับเพื่อประสิทธิภาพ แลกกับการยอมคุมความซ้ำซ้อนเอง
→ 2NF
EMP_PROJ(SSN, PNumber, EName, Hours, PName, PLocation)
- SSN → EName (partial)
- PNumber → PName, PLocation (partial)
- {SSN, PNumber} → Hours (full)
แยกเป็น
EP1(SSN, EName)
EP2(PNumber, PName, PLocation)
EP3(SSN, PNumber, Hours)
→ 3NF
EMP_DEPT(SSN, EName, BDate, Address, DNumber, DName, Dmgr_ssn)
- SSN → …, DNumber
- DNumber → DName, Dmgr_ssn ทำให้เกิด transitive
แยกเป็น
ED1(SSN, EName, BDate, Address, DNumber)
ED2(DNumber, DName, Dmgr_ssn)
→ BCNF
ลงทะเบียน(StudentID, CourseID, Advisor)
- {StudentID, CourseID} → Advisor
- Advisor → CourseID แต่ Advisor ไม่ใช่ superkey
แยกเป็น
(Advisor, CourseID)
(StudentID, Advisor)
TEACH(Student, Course, Instructor) ก็เป็นแบบเดียวกัน คือผ่าน 3NF แต่ไม่ผ่าน BCNF
สไลด์ p.42 บอกว่า (ItemID, Zipcode, City) ที่มี Zipcode → City "ผ่าน 3NF แต่ไม่เป็น BCNF" แต่ถ้าดูตามนิยามในสไลด์เอง Zipcode → City เป็น partial dependency ซึ่งไม่ผ่านตั้งแต่ 2NF แล้ว ถ้าข้อนี้ออกสอบควรถามอาจารย์ให้ชัด
ทดสอบ Lossless Join ด้วย Matrix (Chase)
แถว = ตารางย่อย, คอลัมน์ = attribute ถ้าตารางย่อยมี attribute นั้นใส่ a ถ้าไม่มีใส่ b แล้วไล่ใช้ FD ทีละตัว แถวไหนที่ LHS เท่ากันให้ทำ RHS ให้เท่ากัน ถ้าสุดท้ายมีแถวที่เป็น a ครบทุกช่อง แปลว่า Lossless
R1 = EMP(SSN, ENAME), R2 = PROJECT(PNUMBER, PNAME, PLOCATION), R3 = WORKS_ON(SSN, PNUMBER, HOURS)
| SSN | ENAME | PNUM | PNAME | PLOC | HOURS |
| R1 | a1 | a2 | b13 | b14 | b15 | b16 |
| R2 | b21 | b22 | a3 | a4 | a5 | b26 |
| R3 | a1 | b32→a2 | a3 | b34→a4 | b35→a5 | a6 |
SSN→ENAME (R1 กับ R3 มี SSN เท่ากัน) ทำให้ R3.ENAME เป็น a2 ส่วน PNUMBER→PNAME, PLOC (R2 กับ R3) ทำให้ R3 ได้ a4 และ a5 ผลคือ R3 เป็น a ครบทุกช่อง จึง Lossless
ถ้าแยกเป็น EMP_LOCS(ENAME, PLOCATION) กับ EMP_PROJ1(…) matrix จะไม่เปลี่ยนเลย จึงเป็น Lossy และเกิด spurious tuples
ทางลัด: แยกเป็น 2 ตาราง
{R1, R2} เป็น lossless เมื่อ
(R1 ∩ R2) → (R1 − R2)
หรือ
(R1 ∩ R2) → (R2 − R1)
หมายถึงคอลัมน์ที่ใช้ร่วมกันต้องเป็น key ของตารางใดตารางหนึ่ง
แบบฝึก 4: R(A,B,C), F={A→B} แยกเป็น R1(A,B), R2(A,C) จะได้ R1∩R2 = A และ A→B = R1−R2 จึงเป็น Lossless
คุณสมบัติที่ต้องเช็กหลังแยกตาราง
Attribute preservation, Dependency preservation และ Lossless join
Dangling tuple
แถวที่หาคู่ไม่เจอจะหายไปจาก INNER/NATURAL JOIN ถ้าอยากเก็บไว้ต้องใช้ OUTER JOIN (LEFT/RIGHT/FULL) ซึ่งช่องที่ไม่มีคู่จะเป็น NULL
4NF: Multivalued Dependency
| EName | PName | DName |
|---|
| Smith | X | Anna |
| Smith | X | John |
| Smith | Y | Anna |
| Smith | Y | John |
โปรเจกต์กับผู้ติดตามไม่เกี่ยวกัน แต่ต้องเก็บทุกคู่ผสม EName →→ PName และ EName →→ DName
แก้โดยแยกเป็น EMP_PROJECTS(EName, PName) และ EMP_DEPENDENTS(EName, DName)
MVD จะเป็น trivial เมื่อ Y ⊆ X หรือ X ∪ Y = R และ FD ถือเป็นกรณีพิเศษของ MVD
แบบฝึกในชั้น 1 และ 3 พร้อมแนวคำตอบ
แบบฝึก 1: ORDER_ALL(OrderID, OrderDate, CustomerID, CustomerName, ProductID, ProductName, Qty, UnitPrice) ให้หาข้อมูลที่ซ้ำและยกตัวอย่าง anomaly ทั้ง 3 แบบ
แบบฝึก 3: SALES(OrderID, OrderDate, CustomerID, CustomerName, ProductID, ProductName, UnitPrice, Qty) มี Key = {OrderID, ProductID}
FD: OrderID → OrderDate, CustomerID · CustomerID → CustomerName · ProductID → ProductName, UnitPrice · {OrderID, ProductID} → Qty
2NF: ORDER(OrderID, OrderDate, CustomerID, CustomerName) · PRODUCT(ProductID, ProductName, UnitPrice) · ORDER_LINE(OrderID, ProductID, Qty)
3NF: ORDER(OrderID, OrderDate, CustomerID) + CUSTOMER(CustomerID, CustomerName)
แนวคำตอบนี้คิดเพิ่มเอง ไม่ได้มาจากสไลด์