เข้าใจ Foreign Key (FK) สำหรับเชื่อมตาราง พร้อมตัวอย่างการใช้งาน

Foreign Key (FK) คืออะไร
Foreign Key (FK) คือ คอลัมน์หรือฟีลด์ในตารางที่ใช้สร้างความสัมพันธ์ระหว่างข้อมูลทั้งสองตารางเข้าด้วยกัน (หรือ Join กันนั่นเอง)โดย FK (ทำหน้าที่เป็นตารางลูกหรือ Child Table) จะอ้างอิงไปยัง Primary Key (PK) ที่จะทำหน้าที่เสมือนตารางแม่ (Parent Table) โดยปกติแล้วจะเป็นการเชื่อมความสัมพันธ์แบบ 1-to-M (One-to-Many หรือ Many-to-One)
ตัวอย่างการใช้งาน FK
ผมจะขอยกตัวอย่างฐานข้อมูลบางส่วนของเว็บ E-commerce โดยจะมีการสร้างตาราง Buyer, Order และ Order Item ซึ่งเป็นตารางของเว็บขายสินค้าโดยทั่วไปเลยครับ จะเห็นว่า 1 buyer สามารถมีได้หลาย orders และ 1 order ก็ยังมีได้หลาย items เช่นกัน (ก็คือ 1-to-M) จากภาพด้านล่างแสดงผลไดอะแกรม (ใช้ dbdiagram.io)
ไดอะแกรม
ตาราง Buyer
buyer_id (PK) | buyer_name |
1 | Som Jomson |
2 | Salid Narak |
3 | Korat Sudloh |
ตาราง Order
order_id (PK) | order_date | total_price | buyer_id (FK) |
1 | 2023-10-28 | 2 | 1 |
2 | 2023-10-28 | 2 | 2 |
3 | 2023-10-28 | 5 | 3 |
ตาราง Order Items
order_item_id (PK) | order_id (FK) | item_name | quantity | price |
1 | 1 | อาหารเม็ด | 2 | 300.00 |
2 | 1 | อาหารเปียก | 3 | 150.75 |
3 | 2 | อาหารเปียก | 5 | 251.25 |
4 | 3 | ขนมแมวเลีย | 9 | 180.00 |
5 | 3 | ขนมแมวเลีย | 8 | 160.00 |
จะเห็นว่าเราจะ join ตารางด้วยคอลัมน์ FK ไปที่คอลัมน์ PK หรือ Primary Key (คีย์หลัก) และแน่นอนว่า PK นั้นต้องเป็น ID ที่ unique คือต้องมีความเฉพาะเจาะจง ห้ามซ้ำกันเด็ดขาด (โดยส่วนใหญ่แล้วจะใช้ชนิดข้อมูลแบบ Int และเงื่อนไข Auto-increment)
เมื่อนำไปเขียนเป็นโค้ด SQL
-- สร้างตาราง "Buyer"CREATE TABLE Buyer (buyer_id INT AUTO_INCREMENT NOT NULL PRIMARY KEY,buyer_name VARCHAR(255) NOT NULL);-- สร้างตาราง "Order"CREATE TABLE Order (order_id INT AUTO_INCREMENT NOT NULL PRIMARY KEY,order_date DATE NOT NULL,total_price DECIMAL(10, 2) NOT NULL,buyer_id INT NOT NULL,FOREIGN KEY (buyer_id) REFERENCES Buyer(buyer_id));-- สร้างตาราง "Order Items"CREATE TABLE Order_Items (order_item_id INT AUTO_INCREMENT NOT NULL PRIMARY KEY,order_id INT NOT NULL,item_name VARCHAR(255) NOT NULL,quantity INT NOT NULL,price DECIMAL(10, 2) NOT NULL,FOREIGN KEY (order_id) REFERENCES Order(order_id));
บทความแนะนำอ่านเพิ่มเติม: ภาษา SQL คืออะไร รวมคำสั่ง SQL เบื้องต้น
เงื่อนไข ON DELETE
แล้วถ้าต้องมีการลบแถวหรือ Record ภายในตาราง Primary ล่ะ อะไรจะเกิดขึ้น ?
ตามปกติแล้วเมื่อกำหนด FOREIGN KEY
จะไม่สามารถลบข้อมูลใน Primary Key ได้ ดังนั้นต้องมีการกำหนด Constraint (เงื่อนไข) ก่อนว่า เมื่อกดลบแล้วจะมีอะไรเกิดขึ้นต่อไป เวลานำไปใช้งานก็ลองเลือกเงื่อนไขที่เข้ากับบริบทของ use case จริงกันด้วยนะครับ ซึ่งคำสั่ง ON DELETE
ก็มีอยู่ทั้งหมด 5 ตัว ดังนี้
- CASCADE
- RESTRICT
- ACTION
- SET NULL
- SET DEFAULT
ON DELETE CASCADE
คำสั่ง ON DELETE CASCADE
เป็นคำสั่งที่เมื่อเราลบ ID ตัวใดตัวหนึ่งของ PRIMARY KEY
ออกแล้ว ข้อมูลในทุก ๆ Row ของ ID นั้น ๆ จะถูกลบตามไปด้วยเช่นกัน
ถ้าใครที่ใช้ Back-end เฟรมเวิร์ค ตัวอย่างเช่นในภาพด้านล่างคือ Django framework จะเห็นว่ามีการกำหนดเงื่อนไข on_delete ใน FK ตามแนวคิดที่ได้อธิบายไปข้างต้นเลยครับ (แต่ที่เห็นเขียนแปลกจาก SQL ไปบ้างเพราะว่าเป็นการเขียนในรูปแบบ ORM: Object Relational Mapping)
ใน Django Model มีการกำหนด FK และใน FK มีการกำหนด attribute อย่าง on_delete
ปล.ในคอร์สเรียนพัฒนาเว็บด้วย Python (Django) เราก็มีสอน FK ด้วยครับ
ON DELETE RESTRICT
คำสั่ง ON DELETE RESTRICT
เป็นคำสั่งที่ไม่อนุญาตให้มีการลบแถว (Record)ใด ๆ ก็ตามในตารางที่เป็น Primary Table
ON DELETE NO ACTION
คำสั่ง ON DELETE NO ACTION
เป็นคำสั่งที่ป้องกันการลบแถวใน Primary Table คล้ายกับ RESTRICT
มาก ๆ เลยครับ เพื่อน ๆ อาจจะลองเข้าไปอ่านเพิ่มใน PostgreSQL Doc เพิ่มเติมได้
ON DELETE SET NULL
คำสั่ง ON DELETE SET NULL
เป็นคำสั่งที่ชัดเจนตามชื่อเลยครับ เมื่อมีการลบแถวใน Primary Table ค่าจะเปลี่ยนเป็น Null ทันที
ON DELETE SET DEFAULT
คำสั่ง ON DELETE SET DEFAULT
เป็นคำสั่งที่ค่อนข้างคล้ายกับ Null คือเมื่อมีการลบแถวของ Primary Table ค่าจะถูกคืนกลับไปเป็นค่าเริ่มต้น (Default) ซึ่งถ้าหากจะใช้งานคำสั่งนี้ เราจะต้องกำหนดค่า Default ไว้ก่อนครับ
ประโยชน์ของ Foreign Key ?
- ช่วยในการรักษาความสมบูรณ์และความถูกต้องของข้อมูล (Data Integrity & Accuracy) จากการเชื่อมความสัมพันธ์ระหว่างตาราง ข้อมูลสอดคล้องตรงกัน สมมาตรกันทุกจุด เพราะมีจุดอ้างอิงเดียวกัน แม้การแก้ไขข้อมูลเพียงจุดเดียว ก็ไม่ต้องตามไปแก้ในทุก ๆ ฟีลด์ของ record เพื่อให้ข้อมูลกลับมาตรงกัน (ซึ่งทำให้เกิดโอกาสผิดพลาดได้สูงมากครับ)
- ลดปัญหาความซ้ำซ้อนของข้อมูล
- ทำให้การ query ที่ซับซ้อน หรือไม่ว่าจะเป็นการ sort, search ข้อมูล ได้รวดเร็ว (แต่จะมีข้อเสียตรงที่การ INSERT, UPDATE, DELETE จะช้า)
ก็จบไปแล้วครับสำหรับแนวคิดของ Foreign Key และตัวอย่างการใช้งานจริง หวังว่าจะทำให้เพื่อน ๆ ที่อ่าน มองภาพรวมและสามารถนำแนวคิดและการออกแบบในบทความนี้ไปประยุกต์ใช้งานจริงได้ครับ