Câu 1Bạn đã viết REFERENCES category(cat_id) trên một cột, nhưng một dòng trỏ tới một category không tồn tại vẫn được chèn vào. Nguyên nhân khả dĩ nhất là gì?
Ràng buộc khóa ngoại và hành động tham chiếu (ON DELETE / ON UPDATE)
Với thiết lập cha/con category–item, học REFERENCES cho tính toàn vẹn tham chiếu, vì sao cần PRAGMA foreign_keys=ON để bật kiểm tra, và cách ON DELETE CASCADE / SET NULL / RESTRICT lan truyền việc xóa cha — tất cả thực hành.
Dữ liệu sẽ dùng — category và item
Một ràng buộc khóa ngoại (ràng buộc đảm bảo giá trị của một cột luôn tồn tại làm khóa chính ở bảng khác) là cơ chế bảo vệ tính toàn vẹn tham chiếu giữa các bảng (trạng thái mà mọi dòng được tham chiếu đều thực sự tồn tại).
Nó chặn các mâu thuẫn như "một item trỏ tới một category không tồn tại" hoặc "xóa một category để lại các item của nó treo lơ lửng", ngay ở lớp cơ sở dữ liệu.
Kiểm tra khóa ngoại được bật bằng PRAGMA foreign_keys=ON
Ràng buộc khóa ngoại được khai báo bằng cách viết REFERENCES bảng_cha(cột_cha) trong định nghĩa cột của bảng con.
item.cat_id mang REFERENCES category(cat_id), mã hóa quy tắc: mọi giá trị item.cat_id phải tồn tại trong category.
Tuy nhiên, trong console của khóa học này, kiểm tra khóa ngoại mặc định là tắt (PRAGMA foreign_keys bằng 0).
Khi tắt, `REFERENCES` không thực sự được thực thi và các dòng trỏ tới một category không tồn tại vẫn có thể được chèn.
Để thực sự thực thi ràng buộc, chạy PRAGMA foreign_keys=ON; một lần cho mỗi kết nối.
-- Bật kiểm tra khóa ngoại
PRAGMA foreign_keys=ON;
-- Xác nhận trạng thái (1 nghĩa là đã bật)
PRAGMA foreign_keys;
-- Cố chèn một dòng trỏ tới cat_id=99 (không có trong category)
-- Với foreign_keys=ON, lệnh này bị từ chối với FOREIGN KEY constraint failed
INSERT INTO item VALUES (5,'Mouse',99);
ON DELETE — làm gì với dòng con khi một dòng cha bị xóa
Khi một dòng cha (category) bị xóa, các dòng con (item) đã trỏ tới nó nên xử lý thế nào? Bạn quyết định bằng cách viết một hành động tham chiếu trong mệnh đề REFERENCES.
Có ba hành động tham chiếu phổ biến.
ON DELETE CASCADE nghĩa là xóa cha cũng xóa các con (xóa dây chuyền), ON DELETE SET NULL nghĩa là cột tham chiếu của con được đặt thành NULL và dòng được giữ lại, và ON DELETE RESTRICT nghĩa là bản thân việc xóa cha bị từ chối khi các con vẫn còn tham chiếu.
Nếu bạn không chỉ định hành động, mặc định cũng giống RESTRICT (một cha được tham chiếu không thể bị xóa).
Hành vi nào phù hợp tùy thuộc vào yêu cầu nghiệp vụ.
| Hành động tham chiếu | Khi cha category bị xóa | Điều gì xảy ra với con item |
|---|---|---|
| ON DELETE CASCADE | Bị xóa | Các con bị xóa dây chuyền cùng với nó |
| ON DELETE SET NULL | Bị xóa | Các con vẫn còn, cat_id thành NULL |
| ON DELETE RESTRICT | Không thể xóa — báo lỗi | Việc xóa cha bị từ chối khi vẫn còn con (không bị động đến) |
-- Ví dụ khai báo với ON DELETE SET NULL
CREATE TABLE cat_demo(cat_id INTEGER PRIMARY KEY, cat_name TEXT NOT NULL);
CREATE TABLE item_demo(
item_id INTEGER PRIMARY KEY,
item_name TEXT NOT NULL,
cat_id INTEGER REFERENCES cat_demo(cat_id) ON DELETE SET NULL
);
INSERT INTO cat_demo VALUES (1,'Stationery'),(2,'Electronics');
INSERT INTO item_demo VALUES (1,'Pen',1),(2,'Phone',2),(3,'Cable',2);
-- Xóa cha cat_id=2 biến cat_id của Phone / Cable thành NULL
DELETE FROM cat_demo WHERE cat_id=2;
SELECT item_id, item_name, cat_id FROM item_demo ORDER BY item_id;
SET NULL và RESTRICT — chọn giữa giữ và từ chối
ON DELETE SET NULL giữ dòng con khi cha bị xóa, chỉ thay cột tham chiếu bằng NULL.
Dùng nó khi bạn không muốn xóa bản ghi con, như "giữ item là 'chưa phân loại'."
Nếu cột tham chiếu của con là NOT NULL, không thể ghi NULL, nên các cột dùng với SET NULL phải cho phép NULL.
ON DELETE RESTRICT từ chối bản thân việc xóa cha khi các con vẫn còn tham chiếu.
Dùng nó khi bạn muốn fail-safe (đừng vô tình loại một dòng master còn được tham chiếu).
Khi bạn đã xóa hoặc gán lại mọi con để không còn tham chiếu, cha có thể bị xóa.
Ở bài tập tiếp theo, bạn sẽ thiết lập SET NULL và RESTRICT cạnh nhau trong một console để so sánh.
-- Ví dụ RESTRICT: cha có con không thể bị xóa
CREATE TABLE cat_r(cat_id INTEGER PRIMARY KEY, cat_name TEXT NOT NULL);
CREATE TABLE item_r(
item_id INTEGER PRIMARY KEY,
item_name TEXT NOT NULL,
cat_id INTEGER REFERENCES cat_r(cat_id) ON DELETE RESTRICT
);
INSERT INTO cat_r VALUES (1,'Stationery'),(2,'Electronics');
INSERT INTO item_r VALUES (1,'Pen',1);
-- cat_id=1 được item_r tham chiếu, nên việc xóa bị từ chối
DELETE FROM cat_r WHERE cat_id=1;
-- cat_id=2 không có tham chiếu, nên có thể bị xóa
DELETE FROM cat_r WHERE cat_id=2;
SELECT * FROM cat_r ORDER BY cat_id;
Kiểm tra kiến thức
Hãy trả lời từng câu hỏi một.
Câu 2Hành động tham chiếu nào khiến một dòng con tự động bị xóa khi dòng cha của nó bị xóa?
Câu 3Cột tham chiếu của bảng con phải thỏa điều kiện gì để dùng ON DELETE SET NULL?