SQL Tutorial › Database Design › Constraints & DEFAULT

SQL Constraints and DEFAULT

IntermediateLesson 50 of 6911 min read7 queries run on MySQL 8.4

Constraints are rules the database enforces on every insert and update, so bad data never gets in, no matter which application, script or person writes it. This lesson shows all six MySQL constraints on one table, what each error looks like, and covers DEFAULT in detail. Each constraint then has its own lesson.

Advertisement
In this lesson
  • The six constraints and what each one prevents
  • Column-level vs table-level and named constraints
  • Give columns default values with DEFAULT
  • List, add and drop constraints

The six constraints

ConstraintRuleLesson
NOT NULLthe column must have a valueNOT NULL
UNIQUEno two rows may share a valueUNIQUE
PRIMARY KEYunique + not null; identifies each rowPRIMARY KEY
FOREIGN KEYthe value must exist in another tableFOREIGN KEY
CHECKthe value must satisfy a conditionCHECK
DEFAULTvalue used when none is giventhis lesson

All of them in one table

SQL
CREATE TABLE members (  member_id  INT PRIMARY KEY,  email      VARCHAR(100) NOT NULL UNIQUE,  full_name  VARCHAR(60)  NOT NULL,  dept_id    INT,  age        INT CHECK (age >= 18),  plan       VARCHAR(10)  NOT NULL DEFAULT 'free',  joined_on  DATE         DEFAULT (CURRENT_DATE),  CONSTRAINT fk_member_dept FOREIGN KEY (dept_id) REFERENCES departments (dept_id));INSERT INTO members (member_id, email, full_name, dept_id, age)VALUES (1, 'asha@example.com', 'Asha Rao', 1, 30);SELECT member_id, email, plan, joined_on IS NOT NULL AS has_join_date FROM members;
ResultMySQL 8.4
mysql> CREATE TABLE members (
Query OK, 0 rows affected
mysql> INSERT INTO members (member_id, email, full_name, dept_id, age)
Query OK, 1 row affected
mysql> SELECT member_id, email, plan, joined_on IS NOT NULL AS has_join_date …
member_idemailplanhas_join_date
1asha@example.comfree1
1 row in set
How this query works, step by step
  1. PRIMARY KEY on member_id, NOT NULL UNIQUE on email, CHECK on age, a named FOREIGN KEY on dept_id.
  2. The insert leaves out plan and joined_on, so their defaults are used: 'free' and today’s date.
  3. The SELECT confirms the defaults were applied.

What each violation looks like

Each of these inserts breaks one rule (every block starts from the table above with Asha’s row):

MySQL
CREATE TABLE members (member_id INT PRIMARY KEY, email VARCHAR(100) NOT NULL UNIQUE, full_name VARCHAR(60) NOT NULL, dept_id INT, age INT CHECK (age >= 18), CONSTRAINT fk_member_dept FOREIGN KEY (dept_id) REFERENCES departments (dept_id));INSERT INTO members VALUES (1, 'asha@example.com', 'Asha Rao', 1, 30);INSERT INTO members VALUES (2, 'asha@example.com', 'Second Asha', 1, 25);
ResultMySQL 8.4
mysql> CREATE TABLE members (member_id INT PRIMARY KEY, email VARCHAR(100) NO…
Query OK, 0 rows affected
mysql> INSERT INTO members VALUES (1, 'asha@example.com', 'Asha Rao', 1, 30)
Query OK, 1 row affected
mysql> INSERT INTO members VALUES (2, 'asha@example.com', 'Second Asha', 1, 25)
ERROR 1062 (23000): Duplicate entry 'asha@example.com' for key 'members.email'
MySQL
CREATE TABLE members (member_id INT PRIMARY KEY, email VARCHAR(100) NOT NULL UNIQUE, full_name VARCHAR(60) NOT NULL, dept_id INT, age INT CHECK (age >= 18), CONSTRAINT fk_member_dept FOREIGN KEY (dept_id) REFERENCES departments (dept_id));INSERT INTO members VALUES (3, 'teen@example.com', 'Young User', 1, 15);
ResultMySQL 8.4
mysql> CREATE TABLE members (member_id INT PRIMARY KEY, email VARCHAR(100) NO…
Query OK, 0 rows affected
mysql> INSERT INTO members VALUES (3, 'teen@example.com', 'Young User', 1, 15)
ERROR 3819 (HY000): Check constraint 'members_chk_1' is violated.
MySQL
CREATE TABLE members (member_id INT PRIMARY KEY, email VARCHAR(100) NOT NULL UNIQUE, full_name VARCHAR(60) NOT NULL, dept_id INT, age INT CHECK (age >= 18), CONSTRAINT fk_member_dept FOREIGN KEY (dept_id) REFERENCES departments (dept_id));INSERT INTO members VALUES (4, 'x@example.com', 'Nobody', 42, 30);
ResultMySQL 8.4
mysql> CREATE TABLE members (member_id INT PRIMARY KEY, email VARCHAR(100) NO…
Query OK, 0 rows affected
mysql> INSERT INTO members VALUES (4, 'x@example.com', 'Nobody', 42, 30)
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`company`.`members`, CONSTRAINT `fk_member_dept` FOREIGN KEY (`dept_id`) REFERENCES `departments` (`dept_id`))
How this query works, step by step
  1. Error 1062: the email is already used (UNIQUE).
  2. Error 3819: age 15 fails CHECK (age >= 18).
  3. Error 1452: there is no department 42 (FOREIGN KEY).

Column-level vs table-level

A constraint written next to a column applies to that column. A constraint written as a separate line (table-level) can cover several columns and can be named:

MySQL
CREATE TABLE enrolments (  student_id INT,  course_id  INT,  enrolled   DATE NOT NULL,  CONSTRAINT pk_enrolments PRIMARY KEY (student_id, course_id),  CONSTRAINT chk_enrolled CHECK (enrolled >= '2020-01-01'));
Tip

Name your constraints (fk_member_dept, chk_age). The name appears in error messages and is what you use to drop the constraint later.

DEFAULT

DEFAULT gives a column a value when an INSERT does not supply one. Since MySQL 8.0.13 a default can be an expression in parentheses:

MySQL
CREATE TABLE tasks (  task_id    INT PRIMARY KEY,  title      VARCHAR(50) NOT NULL,  status     VARCHAR(12) NOT NULL DEFAULT 'open',  priority   INT DEFAULT 3,  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,  code       VARCHAR(40) DEFAULT (UUID()));INSERT INTO tasks (task_id, title) VALUES (1, 'Write test plan');INSERT INTO tasks (task_id, title, status, priority) VALUES (2, 'Fix login bug', 'in_progress', 1);INSERT INTO tasks (task_id, title, priority) VALUES (3, 'Review', DEFAULT);SELECT task_id, title, status, priority, created_at IS NOT NULL AS stamped, CHAR_LENGTH(code) AS code_len FROM tasks;
ResultMySQL 8.4
mysql> CREATE TABLE tasks (
Query OK, 0 rows affected
mysql> INSERT INTO tasks (task_id, title) VALUES (1, 'Write test plan')
Query OK, 1 row affected
mysql> INSERT INTO tasks (task_id, title, status, priority) VALUES (2, 'Fix l…
Query OK, 1 row affected
mysql> INSERT INTO tasks (task_id, title, priority) VALUES (3, 'Review', DEFA…
Query OK, 1 row affected
mysql> SELECT task_id, title, status, priority, created_at IS NOT NULL AS sta…
task_idtitlestatusprioritystampedcode_len
1Write test planopen3136
2Fix login bugin_progress1136
3Reviewopen3136
3 rows in set
How this query works, step by step
  1. Task 1 gives only id and title: status 'open', priority 3, a timestamp and a generated UUID are filled in.
  2. Task 2 gives its own status and priority, so the defaults are not used for those.
  3. Task 3 writes the keyword DEFAULT explicitly, which also uses the default value.
Note

An explicit NULL is not replaced by the default: INSERT ... (priority) VALUES (NULL) stores NULL. Add NOT NULL if that must not happen.

See a table’s constraints

MySQL
SELECT constraint_name, constraint_typeFROM information_schema.table_constraintsWHERE table_schema = 'company' AND table_name = 'employees';
ResultMySQL 8.4
CONSTRAINT_NAMECONSTRAINT_TYPE
PRIMARYPRIMARY KEY
employees_ibfk_1FOREIGN KEY
2 rows in set

SHOW CREATE TABLE employees; shows all constraints in the original syntax.

Add and drop constraints later

MySQL
ALTER TABLE products ADD CONSTRAINT chk_price CHECK (price > 0);ALTER TABLE products ALTER stock SET DEFAULT 0;SELECT constraint_name, constraint_type FROM information_schema.table_constraintsWHERE table_schema = 'company' AND table_name = 'products';ALTER TABLE products DROP CHECK chk_price;
ResultMySQL 8.4
mysql> ALTER TABLE products ADD CONSTRAINT chk_price CHECK (price > 0)
Query OK, 9 rows affected
mysql> ALTER TABLE products ALTER stock SET DEFAULT 0
Query OK, 0 rows affected
mysql> SELECT constraint_name, constraint_type FROM information_schema.table_…
CONSTRAINT_NAMECONSTRAINT_TYPE
PRIMARYPRIMARY KEY
chk_priceCHECK
2 rows in set
mysql> ALTER TABLE products DROP CHECK chk_price
Query OK, 0 rows affected

Adding a constraint fails if existing rows already break it, so clean the data first.

Quick quiz

1. Which constraint makes sure a value exists in another table?

2. An INSERT gives NULL explicitly for a column with DEFAULT 3. What is stored?

3. Why name a constraint?

Constraints FAQ

What are constraints in SQL?

Rules defined on columns or tables that the database enforces: NOT NULL, UNIQUE, PRIMARY KEY, FOREIGN KEY, CHECK and DEFAULT.

What is the difference between a primary key and a unique key?

Both prevent duplicates. A table has only one primary key and it cannot be NULL; it can have many unique keys, which allow NULLs.

Can a DEFAULT value be a function?

Yes, since MySQL 8.0.13, as an expression in parentheses, e.g. DEFAULT (UUID()) or DEFAULT (CURRENT_DATE). CURRENT_TIMESTAMP works without parentheses for DATETIME and TIMESTAMP.