SQL CHECK Constraint
A CHECK constraint validates each row against a condition: prices must be positive, ratings between 1 and 5, an end date after its start date. MySQL accepted the syntax for years but ignored it; since MySQL 8.0.16 checks are enforced, so they are now a real tool for keeping data valid.
- Validate values with CHECK conditions
- Write rules across several columns
- Remember how CHECK treats NULL
- Name, add, drop and disable checks
A column check
CREATE TABLE ratings ( rating_id INT PRIMARY KEY, stars INT NOT NULL CHECK (stars BETWEEN 1 AND 5), comment VARCHAR(200));INSERT INTO ratings VALUES (1, 5, 'Great course'), (2, 3, NULL);SELECT * FROM ratings;| rating_id | stars | comment |
|---|---|---|
| 1 | 5 | Great course |
| 2 | 3 | NULL |
CREATE TABLE ratings (rating_id INT PRIMARY KEY, stars INT NOT NULL CHECK (stars BETWEEN 1 AND 5));INSERT INTO ratings VALUES (3, 7);ERROR 3819 (HY000): Check constraint 'ratings_chk_1' is violated.
- Every insert or update is tested with
stars BETWEEN 1 AND 5. - 7 fails, so MySQL rejects the row with error 3819 and names the check (
ratings_chk_1).
A rule across columns
A table-level check can compare columns, and a name makes the error clearer:
CREATE TABLE promotions ( promo_id INT PRIMARY KEY, discount INT NOT NULL, start_date DATE NOT NULL, end_date DATE NOT NULL, CONSTRAINT chk_discount CHECK (discount > 0 AND discount <= 70), CONSTRAINT chk_dates CHECK (end_date >= start_date));INSERT INTO promotions VALUES (1, 20, '2026-01-01', '2026-01-31');INSERT INTO promotions VALUES (2, 10, '2026-03-10', '2026-03-01');ERROR 3819 (HY000): Check constraint 'chk_dates' is violated.
- Promotion 1 passes both checks.
- Promotion 2 ends before it starts, so
chk_datesrejects it, and the error message names it.
A list of allowed values:
CREATE TABLE shipments ( shipment_id INT PRIMARY KEY, status VARCHAR(12) NOT NULL CHECK (status IN ('packed', 'shipped', 'delivered')));INSERT INTO shipments VALUES (1, 'packed'), (2, 'shipped');SELECT * FROM shipments;| shipment_id | status |
|---|---|
| 1 | packed |
| 2 | shipped |
CHECK and NULL
A check passes when its condition is UNKNOWN, which happens when a value is NULL:
CREATE TABLE ratings (rating_id INT PRIMARY KEY, stars INT CHECK (stars BETWEEN 1 AND 5));INSERT INTO ratings VALUES (1, NULL);SELECT * FROM ratings;| rating_id | stars |
|---|---|
| 1 | NULL |
The row is accepted because NULL BETWEEN 1 AND 5 is UNKNOWN, not FALSE. Add NOT NULL when the value is required.
Add, drop and disable a check
ALTER TABLE products ADD CONSTRAINT chk_stock CHECK (stock >= 0);SELECT constraint_name, check_clause FROM information_schema.check_constraintsWHERE constraint_schema = 'company';| CONSTRAINT_NAME | CHECK_CLAUSE |
|---|---|
| chk_stock | (`stock` >= 0) |
ALTER TABLE products ADD CONSTRAINT chk_stock CHECK (stock >= 0);UPDATE products SET stock = -5 WHERE product_id = 1;ERROR 3819 (HY000): Check constraint 'chk_stock' is violated.
ALTER TABLE products DROP CHECK chk_stock;removes it.ALTER TABLE products ALTER CHECK chk_stock NOT ENFORCED;keeps it but stops checking (useful for bulk loads).
A check may use only the row’s own columns and deterministic functions. It cannot use subqueries, other tables, NOW() or user variables. For rules that involve other tables, use a foreign key or a trigger.
Quick quiz
1. From which MySQL version are CHECK constraints enforced?
Before 8.0.16 MySQL parsed and ignored them.
2. A column has CHECK (age >= 18). Is a NULL age accepted?
Add NOT NULL to require a value.
3. Can a CHECK constraint compare two columns of the same row?
Table-level checks can use several columns.
CHECK FAQ
Does MySQL support CHECK constraints?
Yes. MySQL 8.0.16 and later enforce CHECK constraints; older versions ignored them.
Can a CHECK constraint reference another table?
No. Checks see only the current row. Use a foreign key or a trigger for cross-table rules.
What is the difference between CHECK and ENUM?
ENUM is a data type that restricts a column to a list. A CHECK can express any row-level rule, including ranges and comparisons between columns, and keeps the column a normal type.