Create a CHECK Constraint in MySQL

As of MySQL 8.0.16, we can create CHECK constraints in MySQL.

A CHECK constraint is a type of integrity constraint. It specifies a search condition to check the value being entered into a row. If the value being entered violates the CHECK constraint, then the result of the search condition is FALSE and an error occurs (unless the IGNORE clause is used in the SQL statement, in which case a warning is reported and the offending row is skipped).

The ability to create CHECK constraints was introduced in MySQL 8.0.16. Prior to version 8.0.16, MySQL actually allowed a limited version of CHECK constraint syntax, but it was completely ignored (no CHECK constraint was created or evaluated).

Syntax

The syntax goes like this:

[CONSTRAINT [symbol]] CHECK (expr) [[NOT] ENFORCED]

Where symbol is the name of the constraint. This is optional. If we don’t provide a name for the constraint, then MySQL will generate one.

We can apply the above syntax in a CREATE TABLE statement or an ALTER TABLE statement.

Example

Here’s an example of creating a table with a column constraint:

CREATE TABLE Products (
    ProductId int,
    ProductName varchar(255),
    ProductPrice decimal(8,2) CONSTRAINT ProductPrice_chk CHECK (ProductPrice > 0),
    RecommendedPrice decimal(8,2)
);

In this example I created a CHECK constraint against the ProductPrice column. The CHECK constraint checks that any value entered into the ProductPrice column is greater than zero.

Optional Name and CONSTRAINT Keyword

In the above example I named the CHECK constraint ProductPrice_chk. Any error that’s generated will use that name. I could have omitted the name, in which case MySQL would have generated its own name for me.

The CONSTRAINT keyword is also optional.

Therefore, we could shorten the above code to the following:

CREATE TABLE Products (
    ProductId int,
    ProductName varchar(255),
    ProductPrice decimal(8,2) CHECK (ProductPrice > 0),
    RecommendedPrice decimal(8,2)
);

In which case MySQL would create its own name for the constraint.

In any case, I’ve already created the constraint and specified a name for it.

Violate the CHECK Constraint

Here’s what happens when I try to insert a value that violates the CHECK constraint:

INSERT INTO Products 
VALUES ( 1, "Hammer", 0, 11.55 );

Result:

ERROR 3819 (HY000): Check constraint 'ProductPrice_chk' is violated.

As expected, I get an error. This is because I tried to insert zero into the column, but the CHECK constraint specifies that the value must be greater than zero.

Let’s change the value to one that conforms to the CHECK constraint:

INSERT INTO Products 
VALUES ( 1, "Hammer", 20, 11.55 );

Result:

Query OK, 1 row affected (0.00 sec)

This time it succeeded.

Note that the CHECK constraint works like this because it is enforced by default. I could have explicitly used the ENFORCED keyword when I created the constraint, but CHECK constraints are enforced by default. If I had used NOT ENFORCED, then I wouldn’t have gotten the error when I tried to insert data that violated the CHECK constraint. Instead, the row would have been inserted fine, but with data that violates the CHECK constraint.

Check Multiple Columns

We can also add CHECK constraints to check multiple columns. For example, we can create a CHECK constraint that checks whether one column’s value is greater than another column’s value.

Let’s add a CHECK constraint to our table:

ALTER TABLE Products ADD CHECK (ProductPrice > RecommendedPrice);

This added a CHECK constraint to check that any value entered into the ProductPrice column is greater than the value in the RecommendedPrice column.

Now let’s try to add a value that violates our newly created CHECK constraint:

INSERT INTO Products 
VALUES ( 2, "Wrench", 9, 15 );

Result:

ERROR 3819 (HY000): Check constraint 'products_chk_1' is violated.

As expected, we get an error.

Note that the name of the constraint is products_chk_1. This was named by MySQL because I didn’t provide a name for the constraint when I created it.

Either way, the constraint works as expected. If we check the table’s data, we can see that it only contains one row (the hammer from the previous example):

SELECT * FROM Products;

Result:

+-----------+-------------+--------------+------------------+
| ProductId | ProductName | ProductPrice | RecommendedPrice |
+-----------+-------------+--------------+------------------+
|         1 | Hammer      |        20.00 |            11.55 |
+-----------+-------------+--------------+------------------+

How to Downgrade the Error

MySQL provides us with the IGNORE clause that can be used to downgrade the error to a warning:

INSERT IGNORE INTO Products 
VALUES ( 2, "Wrench", 9, 15 );

Result:

Query OK, 0 rows affected, 1 warning (0.00 sec)

So we didn’t get the error like in the previous example. The message tells us that the query was OK, that zero rows were affected, and we got a warning instead.

Let’s check the warning:

SHOW WARNINGS;

Result:

+---------+------+------------------------------------------------+
| Level   | Code | Message                                        |
+---------+------+------------------------------------------------+
| Warning | 3819 | Check constraint 'products_chk_1' is violated. |
+---------+------+------------------------------------------------+

As expected.

Let’s check the table’s contents:

SELECT * FROM Products;

Result:

+-----------+-------------+--------------+------------------+
| ProductId | ProductName | ProductPrice | RecommendedPrice |
+-----------+-------------+--------------+------------------+
|         1 | Hammer      |        20.00 |            11.55 |
+-----------+-------------+--------------+------------------+

So the violating row still wasn’t inserted.

Skipping the Violating Row

When using the IGNORE clause, MySQL skips any rows that violate the CHECK constraint, but it inserts rows that conform. So if we expand our INSERT statement to include more rows, the conforming rows are inserted.

First, here it is without the IGNORE clause:

INSERT INTO Products VALUES 
    ( 2, "Rope", 7, 5 ),
    ( 3, "Wrench", 9, 15 ),
    ( 4, "Saw", 20, 15 );

Result:

ERROR 3819 (HY000): Check constraint 'products_chk_1' is violated.

And let’s check the table:

SELECT * FROM Products;

Result:

+-----------+-------------+--------------+------------------+
| ProductId | ProductName | ProductPrice | RecommendedPrice |
+-----------+-------------+--------------+------------------+
|         1 | Hammer      |        20.00 |            11.55 |
+-----------+-------------+--------------+------------------+

Nothing was inserted.

Now let’s add the IGNORE clause:

INSERT IGNORE INTO Products VALUES 
    ( 2, "Rope", 7, 5 ),
    ( 3, "Wrench", 9, 15 ),
    ( 4, "Saw", 20, 15 );

Result:

Records: 2  Duplicates: 0  Warnings: 1

So we get a warning instead of an error. Also, we can see that two rows were inserted, based on the message.

And let’s check the table:

SELECT * FROM Products;

Result:

+-----------+-------------+--------------+------------------+
| ProductId | ProductName | ProductPrice | RecommendedPrice |
+-----------+-------------+--------------+------------------+
|         1 | Hammer      |        20.00 |            11.55 |
|         2 | Rope        |         7.00 |             5.00 |
|         4 | Saw         |        20.00 |            15.00 |
+-----------+-------------+--------------+------------------+

As expected, the two rows that conform to the CHECK constraint were inserted. The row that violates the CHECK constraint was skipped.