Học bằng cách đọc theo thứ tự

Ràng buộc cột — NOT NULL / UNIQUE / CHECK / PRIMARY KEY

Thiết kế các bảng như app_member với PRIMARY KEY / NOT NULL / UNIQUE / CHECK, rồi trải nghiệm cách các INSERT vi phạm ràng buộc bị từ chối với lỗi constraint failed — thực hành trực tiếp trên trình duyệt.

Ràng buộc cột là gì — chặn dữ liệu xấu ngay ở lớp DB

Một ràng buộc cột (quy tắc đầu vào gắn với một cột) là "điều kiện cho các giá trị có thể đi vào cột này", được viết trong CREATE TABLE.

Bài viết này đề cập bốn loại lớn — PRIMARY KEY (định danh duy nhất một dòng),

NOT NULL (cấm NULL), UNIQUE (cấm trùng), và CHECK (cấm các giá trị không thỏa một điều kiện).

Khác với các bài trước, không có dữ liệu nào được nạp sẵn — thay vào đó, bạn tự dựng các bảng trong các bài tập và chạy có chủ ý các INSERT vi phạm ràng buộc để trải nghiệm lỗi.

Vai trò của bốn ràng buộc cột
PRIMARY KEYĐịnh danh duy nhất một dòngKhông NULL, không trùngNOT NULLCấm NULLBắt buộc phải nhậpUNIQUECấm giá trị trùng(cho phép NULL)CHECKCấm các giá trị làmbiểu thức không đúng
Mỗi ràng buộc giới hạn "giá trị nào có thể đi vào cột". Các câu INSERT / UPDATE vi phạm sẽ bị từ chối với lỗi, ngăn dữ liệu xấu vào bảng.

Khai báo bảng kèm ràng buộc

Ràng buộc đi sau kiểu trong định nghĩa cột.

Dạng là tên_cột kiểu ràng_buộc ràng_buộc ..., và bạn có thể xếp chồng nhiều ràng buộc lên một cột.

Gắn PRIMARY KEY vào một cột INTEGER biến nó thành khóa chính định danh duy nhất một dòng.

NOT NULL cấm bỏ qua giá trị, UNIQUE cấm cùng giá trị với một dòng khác, và CHECK (biểu_thức) cấm bất kỳ giá trị nào không làm biểu thức đúng.

Bảng member dưới đây đặt member_id làm khóa chính, handle (tên người dùng) là NOT NULL kèm UNIQUE, và gắn CHECK "không âm" cho age.

-- Mẫu định nghĩa bảng có ràng buộc (chỉ đọc)
CREATE TABLE IF NOT EXISTS member (
  member_id INTEGER PRIMARY KEY,        -- khóa chính: duy nhất và không NULL
  handle    TEXT    NOT NULL UNIQUE,    -- bắt buộc và không trùng
  email     TEXT    UNIQUE,             -- không trùng (cho phép NULL)
  age       INTEGER CHECK (age >= 0)    -- chỉ giá trị không âm
);

-- Một dòng thỏa mọi ràng buộc thì vào được mà không gặp vấn đề
INSERT INTO member (member_id, handle, email, age)
VALUES (1, 'alice', 'alice@example.com', 30);

Hãy hình dung yêu cầu: "thiết kế bảng member với ràng buộc để dữ liệu xấu không thể lọt vào." (Chạy đúng thì phần giải thích sẽ hiện ra.)

① Bắt đầu bằng DROP TABLE IF EXISTS app_member; để xóa phiên bản cũ, rồi dựng bảng app_member. Các cột: member_id (số nguyên, khóa chính), handle (text, bắt buộc và không trùng), email (text, không trùng nhưng cho phép NULL), age (số nguyên, chỉ không âm).

② INSERT một dòng thỏa mọi ràng buộc (ví dụ member_id 1 / handle chuỗi ascii / email chuỗi ascii / age là số nguyên ≥ 0).

③ Chạy SELECT * FROM app_member; để xác nhận một dòng đã vào.

SQL Editor

Chạy truy vấn để xem kết quả

Vi phạm ràng buộc sinh lỗi — NOT NULL và UNIQUE

Công việc của một ràng buộc là từ chối các thao tác ghi vi phạm.

Cố đặt NULL (hoặc bỏ qua giá trị) vào một cột NOT NULL sẽ sinh lỗi NOT NULL constraint failed, và chèn một giá trị đã tồn tại vào cột UNIQUE / PRIMARY KEY sẽ sinh lỗi UNIQUE constraint failed.

Dòng INSERT bị lỗi sẽ không vào bảng.

Luồng của một INSERT vi phạm ràng buộc
INSERT thỏaràng buộcDB chấp nhậnDòng được thêmINSERT vi phạmràng buộclỗi constraintfailedDòng không được thêm(dữ liệu xấu bị chặn)
Các dòng thỏa ràng buộc thì vào bình thường; các dòng vi phạm bị từ chối với lỗi và không bao giờ được thêm. Một lỗi là bằng chứng rằng "ràng buộc đang hoạt động".
-- Vi phạm NOT NULL: cố đặt NULL vào handle (chỉ đọc)
INSERT INTO member (member_id, handle, age) VALUES (2, NULL, 20);
--> Lỗi: NOT NULL constraint failed: member.handle

-- Vi phạm UNIQUE: cố chèn cùng handle với một dòng hiện có (chỉ đọc)
INSERT INTO member (member_id, handle, age) VALUES (3, 'alice', 25);
--> Lỗi: UNIQUE constraint failed: member.handle

Hãy hình dung yêu cầu: "xác minh tác dụng của việc đặt handle là bắt buộc bằng cách kích hoạt một INSERT vi phạm." Bài tập này thành công nếu bạn nhận được lỗi (bằng chứng rằng ràng buộc đang chặn dữ liệu xấu đúng cách).

DELETE FROM app_member; ở đầu sẽ làm trống app_member từ Bài tập 1 (để chạy lại cho ra cùng kết quả).

② Viết một INSERT đặt NULL vào cột NOT NULL handle có chủ ý (ví dụ member_id 1 / handle NULL / email NULL / age 20).

③ Chạy và xác nhận bạn nhận được lỗi NOT NULL constraint failed: app_member.handle. Để ý rằng thông báo lỗi bao gồm bảng nào và cột nào bị vi phạm.

SQL Editor

Chạy truy vấn để xem kết quả

Hãy hình dung yêu cầu: "xác minh tác dụng của việc cấm trùng trên handle bằng cách kích hoạt một INSERT vi phạm." Bài tập này cũng thành công nếu bạn nhận được lỗi.

DELETE FROM app_member; ở đầu sẽ làm trống app_member từ Bài tập 1.

② Cho dòng 1, viết một INSERT của riêng bạn với handle đặt là 'alice' (ví dụ member_id 1 / handle 'alice' / email 'alice@example.com' / age 30).

③ Cho dòng 2, viết một INSERT khác với cùng handle'alice' (bạn có thể thay đổi member_idemail).

④ Chạy và xác nhận dòng 1 vào bình thường, còn dòng 2 sinh UNIQUE constraint failed: app_member.handle.

SQL Editor

Chạy truy vấn để xem kết quả

Ràng buộc CHECK — diễn tả điều kiện giá trị bằng biểu thức

CHECK (biểu_thức) là ràng buộc dùng một biểu thức để mô tả giá trị nào có thể đi vào cột.

CHECK (age >= 0) từ chối tuổi âm, CHECK (price > 0) từ chối giá bằng 0 hoặc nhỏ hơn.

Một mẫu hay gặp là CHECK (status IN ('active','inactive')), giới hạn tập giá trị cho phép.

Nếu biểu thức không đánh giá là đúng, INSERT / UPDATE bị từ chối với lỗi CHECK constraint failed.

Bài tập cuối của bài viết này dùng CHECK (age >= 0)CHECK (status IN (...)) để trải nghiệm cách CHECK từ chối vi phạm.

Cách CHECK đánh giá đúng/sai
Giá trị INSERTĐánh giá biểu thứcKết quảage = 30age >= 0→ trueChấp nhậnDòng được thêmage = -5age >= 0→ falseCHECKconstraint failedstatus ='banned'status IN('active','inactive')→ falseCHECKconstraint failed
Biểu thức CHECK được đánh giá cho mỗi dòng INSERT. Đúng → chấp nhận, ngược lại → từ chối với CHECK constraint failed.
-- Giới hạn phạm vi hoặc tập giá trị qua CHECK (chỉ đọc)
CREATE TABLE IF NOT EXISTS product (
  product_id INTEGER PRIMARY KEY,
  price      INTEGER CHECK (price > 0),
  status     TEXT    CHECK (status IN ('active','inactive'))
);

-- price <= 0 bị từ chối với CHECK constraint failed
INSERT INTO product (product_id, price, status) VALUES (1, -100, 'active');

Hãy hình dung yêu cầu: "xác minh tác dụng của việc gắn CHECK không âm vào age bằng cách kích hoạt một INSERT vi phạm." Lỗi là điều kiện thành công.

DELETE FROM app_member; ở đầu sẽ làm trống app_member từ Bài tập 1.

② Viết INSERT của riêng bạn đặt một giá trị âm (ví dụ -5) vào age. Vì handle là NOT NULL, hãy đặt nó một giá trị không-NULL.

③ Chạy và xác nhận bạn nhận được lỗi CHECK constraint failed: app_member.age. Tùy chọn thử đổi age thành một giá trị không âm và xem INSERT khi đó thành công.

SQL Editor

Chạy truy vấn để xem kết quả

Hãy hình dung yêu cầu: "giới hạn cột status của member chỉ ở hai giá trị 'active' / 'inactive'." Đây là bài tập cuối của bài viết — lỗi là điều kiện thành công.

① Bắt đầu bằng DROP TABLE IF EXISTS app_member_status; để xóa phiên bản cũ, rồi tự dựng bảng app_member_status. Hai cột: member_id (số nguyên, khóa chính) và status (text, bắt buộc và chỉ cho phép 'active' hoặc 'inactive').

② Là dòng 1, INSERT một trong các giá trị cho phép ('active' hoặc 'inactive').

③ Là dòng 2, INSERT 'banned', không nằm trong danh sách cho phép.

④ Chạy và xác nhận dòng 1 vào được và dòng 2 sinh CHECK constraint failed: app_member_status.status.

SQL Editor

Chạy truy vấn để xem kết quả
QUIZ

Kiểm tra kiến thức

Hãy trả lời từng câu hỏi một.

Câu 1Về một cột được khai báo handle TEXT NOT NULL UNIQUE, phát biểu nào đúng?

Câu 2Điều gì xảy ra khi bạn cố INSERT một giá trị đã tồn tại vào cột UNIQUE?

Câu 3Ràng buộc nào thích hợp nhất để diễn tả quy tắc nghiệp vụ "chỉ cho phép giá lớn hơn 0" trong định nghĩa bảng?