Ràng buộc CHECK — kiểu như một "bảo vệ" ở cửa bảng của bạn vậy. Nó đảm bảo rằng dữ liệu bạn thêm vào bảng phải thỏa mãn một số điều kiện nhất định. Nếu bạn cố nhét vào bảng dữ liệu không hợp lệ, database sẽ từ chối nhận luôn.
Hãy tưởng tượng bạn muốn mở một cửa hàng ở Đức. Nhưng theo luật thì chủ nhật là ngày nghỉ, không được bán hàng hôm đó. Ràng buộc này giống như CHECK trong database. Bạn thử nhập lịch mở cửa: mở 7 ngày/tuần, hệ thống sẽ trả lời ngay: "Nein, nein, chủ nhật là vi phạm. Lịch này không qua kiểm tra đâu!"
Database cũng vậy: nếu bạn nhập giá trị vi phạm quy tắc CHECK, hệ thống sẽ chặn lại, không cho "lỗi logic" lọt vào dữ liệu.
Tại sao cần CHECK?
- Giữ chất lượng dữ liệu:
CHECKngăn không cho dữ liệu sai hoặc không hợp lý lọt vào bảng. - Giảm nguy cơ lỗi: thay vì phải tự kiểm tra dữ liệu trước khi insert, bạn có thể giao việc này cho database.
- Tự động hóa logic: quy tắc kiểm tra có thể nhúng thẳng vào cấu trúc database, không cần xử lý ở code ứng dụng.
CHECK hoạt động thế nào?
Ràng buộc CHECK được đặt khi tạo bảng hoặc thêm sau bằng lệnh ALTER TABLE. Đây là cú pháp cơ bản:
CREATE TABLE bảng (
cột kiểu_dữ_liệu CHECK (điều_kiện)
);
điều_kiện — là biểu thức logic phải đúng với mọi giá trị trong cột đó. Nếu điều kiện sai, database sẽ báo lỗi.
Ví dụ 1: Kiểm tra giá trị trong khoảng
Giả sử ta tạo bảng students, tuổi sinh viên (age) phải từ 16 đến 100:
CREATE TABLE students (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
age INTEGER CHECK (age >= 16 AND age <= 100)
);
Bây giờ, nếu bạn thử insert sinh viên tuổi 12, database sẽ "bắt tận tay":
INSERT INTO students (name, age)
VALUES ('Maria Chi', 12);
Lỗi:
ERROR: new row for relation "students" violates check constraint "students_age_check"
DETAIL: Failing row contains (1, Maria Chi, 12).
Đấy, database ở đây nghiêm lắm. Chưa đủ 16 tuổi thì khỏi vào nhé.
Ví dụ 2: Kiểm tra định dạng dữ liệu
Giả sử có bảng emails lưu danh sách email. Ta muốn chắc chắn địa chỉ phải có ký tự @ (cách kiểm tra đơn giản thôi):
CREATE TABLE emails (
id SERIAL PRIMARY KEY,
email VARCHAR(255) CHECK (email LIKE '%@%')
);
Thử thêm một địa chỉ sai định dạng, không có @ xem sao:
INSERT INTO emails (email)
VALUES ('notanemail.com');
Lỗi:
ERROR: new row for relation "emails" violates check constraint "emails_email_check"
DETAIL: Failing row contains (1, notanemail.com).
Muốn tránh lỗi thì mọi email phải có ký tự @:
INSERT INTO emails (email)
VALUES ('example@student.com');
Query này sẽ chạy thành công.
Ví dụ 3: Kiểm tra điều kiện nhiều cột
Ràng buộc CHECK không chỉ kiểm tra một cột, mà còn có thể kiểm tra biểu thức logic liên quan nhiều cột. Ví dụ với bảng employees, lương (salary) phải lớn hơn thưởng (bonus):
CREATE TABLE employees (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
salary NUMERIC CHECK (salary > 0),
bonus NUMERIC CHECK (bonus >= 0),
CHECK (salary > bonus)
);
Bây giờ, nếu ai đó cố thêm nhân viên có thưởng lớn hơn lương, database sẽ không cho phép:
INSERT INTO employees (name, salary, bonus)
VALUES ('Otto Lin', 3000, 4000);
Lỗi:
ERROR: new row for relation "employees" violates check constraint "employees_salary_bonus_check"
DETAIL: Failing row contains (1, Otto Lin, 3000, 4000).
Ứng dụng thực tế
Ràng buộc CHECK rất hữu ích khi business logic của bạn gắn chặt với giới hạn dữ liệu. Ví dụ:
- Shop online: cấm thêm sản phẩm giá âm.
- Nền tảng giáo dục: kiểm tra tuổi học viên khi đăng ký khóa học.
- Hệ thống y tế: đảm bảo nhiệt độ cơ thể bệnh nhân nằm trong giới hạn cho phép.
Những kiểm tra này không chỉ là lớp bảo mật phụ, mà còn tiết kiệm thời gian và công sức cho dev lẫn user.
Lưu ý và lỗi thường gặp
Khi dùng CHECK, nhớ mấy điểm sau:
Biểu thức logic trong
CHECKphải đúng với mọi dòng trong bảng. Nếu có dòng nào vi phạm, trước khi thêm ràng buộc phải sửa lại dòng đó.Kiểm tra sẽ không thực hiện nếu giá trị insert là
NULL. Nói cách khác,CHECK (age >= 18)sẽ không báo lỗi vớiage = NULL. Vì bất kỳ biểu thức nào cóNULLđều thành "không xác định". Nếu muốn cấmNULL, hãy thêmNOT NULL.Điều kiện phức tạp trong
CHECKcó thể làm chậm insert/update, nhất là với bảng lớn.
GO TO FULL VERSION