Besides the nice trigger solution by @Rolando, there's another workaround of this problem in MySQL (until CHECK
constraints are implemented).
How to emulate some CHECK
constraints in MySQL
So, if you prefer referential integrity constraints and want to avoid triggers (because of the issues in MySQL when you have both in your tables), you can use another small reference table:
CREATE TABLE age_allowed
( age TINYINT UNSIGNED NOT NULL
, PRIMARY KEY (age)
) ENGINE = InnoDB ;
Fill it with 20 rows:
INSERT INTO age_allowed
(age)
VALUES
(0), (1), (2), (3), ..., (19) ;
Then your table would be:
CREATE TABLE test
( id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT
, age TINYINT UNSIGNED NOT NULL
, PRIMARY KEY (id)
, CONSTRAINT age_allowed__in__test
FOREIGN KEY (age)
REFERENCES age_allowed (age)
) ENGINE = InnoDB ;
You'll have to remove write access to the age_allowed
table, to avoid accidental adding or removing of rows.
This trick will not work with FLOAT
datatype columns, unfortunately (too many values between 0.0
and 20.0
).
How to emulate arbitrary CHECK
constraints in MySQL (5.7) and MariaDB (from 5.2 up to 10.1)
Since MariaDB added computed columns in their 5.2 version (GA release: 2010-11-10) and MySQL in 5.7 (GA release: 2015-10-21) - which they call them VIRTUAL
and GENERATED
respectively - that can be persisted, i.e. stored in the table - they call them PERSISTENT
and STORED
respectively - we can use them to simplify the above solution and even better, extend it to emulate/enforce arbitrary CHECK
constraints):
As above, we will need a help table but with a single row this time that will be acting as an "anchor" table. Even better, this table can be used for any number of CHECK
constraints.
We then add a computed column that evaluates to either TRUE
/ FALSE
/ UNKNOWN
, exactly as a CHECK
constraint would - but this column has a FOREIGN KEY
constraint to our anchor table. If the condition/column evaluates to FALSE
for some rows, the rows are rejected, due to the FK.
If the condition/column evaluates to TRUE
or UNKNOWN
(NULL
), the rows are not rejected, exactly as it should happen with CHECK
constraints:
CREATE TABLE truth
( t BIT NOT NULL,
PRIMARY KEY (t)
) ENGINE = InnoDB ;
-- Put a single row:
INSERT INTO truth (t)
VALUES (TRUE) ;
-- Then your table would be:
-- (notice the change to `FLOAT`, to prove that we don't need)
-- (to restrict the solution to a small type)
CREATE TABLE test
( id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
age FLOAT NOT NULL,
age_is_allowed BIT -- GENERATED ALWAYS
AS (age >= 0 AND age < 20) -- our CHECK constraint
STORED,
PRIMARY KEY (id),
CONSTRAINT check_age_must_be_non_negative_and_less_than_20
FOREIGN KEY (age_is_allowed)
REFERENCES truth (t)
) ENGINE = InnoDB ;
The example is for MySQL 5.7 version. In MariaDB (versions 5.2+ up to 10.1), we just need to modify the syntax and declare the column as PERSISTENT
instead of STORED
. In version 10.2 the STORED
keyword was added as well, so the example above works in both flavours (MySQL and MariaDB) for the latest versions.
If we want to enforce many CHECK
constraints (which is common in many designs), we just have to add a computed column and a foreign key for each one of them. We only need one truth
table in the database. It should have one row inserted and then all write access removed.
In the latest MariaDB however, we don't have to perform all these acrobatics any more, as CHECK
constraints have been implemented in version 10.2.1 (alpha release : 2016-Jul-04)!
The current 10.2.2 version is still a beta version but it seems that the feature will be available in the first stable release of the MariaDB 10.2 series.