00:00

MySQL Constraints

Last updated: 6 min read

Constraints are rules attached to tables and columns that MySQL enforces on every INSERT and UPDATE. They stop bad data at the door, such as a patient without a name, two patients with the same ID, a negative bill or an appointment for a doctor who doesn't exist. Because the database enforces them, every application that writes to it is protected.

1. Why Use Constraints?

Constraints guard every write to a tableAn INSERT or UPDATE passes through six checks before reaching the table: NOT NULL, DEFAULT, UNIQUE, PRIMARY KEY, CHECK and FOREIGN KEY. If any check fails, the whole statement is rejected and nothing is written.INSERT /UPDATENOT NULLvalue present?DEFAULTfill if missingUNIQUEno duplicate?PRIMARY KEYunique + not nullCHECKrule satisfied?FOREIGN KEYparent exists?Tableonly valid rowsany failed check → the statement is rejected, nothing is written
Figure 1: constraints are rules the database enforces on every change, whatever program sends it.
  • Correctness: invalid data never gets in, so reports and code can trust it.
  • One place for rules: the Java app, a Python script and someone at the command line all obey the same rules.
  • Clear design: keys document how tables relate.
  • Speed: PRIMARY KEY and UNIQUE constraints create indexes that make lookups fast.

2. The Six Constraints in MySQL

ConstraintRuleExampleTutorial
NOT NULLColumn must have a valuename VARCHAR(50) NOT NULLNOT NULL
UNIQUENo two rows share the value (NULLs allowed)email VARCHAR(80) UNIQUEUNIQUE
PRIMARY KEYUnique + NOT NULL row identifier, one per tablepatient_id INT PRIMARY KEYPrimary Key
FOREIGN KEYValue must exist in another tableREFERENCES doctors(doctor_id)Foreign Key
CHECKA condition must be true (8.0.16+)CHECK (fee >= 0)CHECK
DEFAULTValue used when none is givenstatus VARCHAR(10) DEFAULT 'booked'DEFAULT
Strictly speaking DEFAULT doesn't reject anything, but it is defined with the column and is usually taught with constraints.

3. All Constraints in One Example

all-constraints.sql
CREATE TABLE doctors (
  doctor_id INT PRIMARY KEY,
  name      VARCHAR(50) NOT NULL,
  reg_no    VARCHAR(12) NOT NULL UNIQUE
);

CREATE TABLE appointments (
  appt_id    INT AUTO_INCREMENT PRIMARY KEY,
  patient    VARCHAR(50) NOT NULL,
  doctor_id  INT NOT NULL,
  appt_date  DATE NOT NULL,
  fee        DECIMAL(8,2) NOT NULL DEFAULT 500,
  status     VARCHAR(10) NOT NULL DEFAULT 'booked',
  CONSTRAINT fk_appt_doctor FOREIGN KEY (doctor_id) REFERENCES doctors(doctor_id),
  CONSTRAINT chk_fee CHECK (fee >= 0),
  CONSTRAINT chk_status CHECK (status IN ('booked', 'done', 'cancelled')),
  CONSTRAINT uq_slot UNIQUE (doctor_id, appt_date, patient)
);

INSERT INTO doctors VALUES (1, 'Dr. Mehta', 'MH-10234'), (2, 'Dr. Iyer', 'TN-55871');
INSERT INTO appointments (patient, doctor_id, appt_date) VALUES ('Anaya', 1, '2026-03-10');
SELECT * FROM appointments;

Result: 1 row

appt_idpatientdoctor_idappt_datefeestatus
1Anaya12026-03-10500.00booked

The defaults filled in fee and status. Now try to break each rule:

break-the-rules.sql
INSERT INTO appointments (patient, doctor_id, appt_date) VALUES (NULL, 1, '2026-03-11');
INSERT INTO doctors VALUES (3, 'Dr. Rao', 'MH-10234');
INSERT INTO doctors VALUES (1, 'Dr. Khan', 'DL-77001');
INSERT INTO appointments (patient, doctor_id, appt_date) VALUES ('Bose', 9, '2026-03-11');
INSERT INTO appointments (patient, doctor_id, appt_date, fee) VALUES ('Bose', 2, '2026-03-11', -50);
INSERT INTO appointments (patient, doctor_id, appt_date, status) VALUES ('Bose', 2, '2026-03-11', 'maybe');
INSERT INTO appointments (patient, doctor_id, appt_date) VALUES ('Anaya', 1, '2026-03-10');
Output
ERROR 1048 (23000): Column 'patient' cannot be null
ERROR 1062 (23000): Duplicate entry 'MH-10234' for key 'doctors.reg_no'
ERROR 1062 (23000): Duplicate entry '1' for key 'doctors.PRIMARY'
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`hospital`.`appointments`, CONSTRAINT `fk_appt_doctor` FOREIGN KEY (`doctor_id`) REFERENCES `doctors` (`doctor_id`))
ERROR 3819 (HY000): Check constraint 'chk_fee' is violated.
ERROR 3819 (HY000): Check constraint 'chk_status' is violated.
ERROR 1062 (23000): Duplicate entry '1-2026-03-10-Anaya' for key 'appointments.uq_slot'

Every bad row was rejected with a clear message, and the table still holds only the one valid appointment.

4. Column-Level vs Table-Level Syntax

Column-level (inside the column definition)Table-level (after the columns)
email VARCHAR(80) UNIQUEUNIQUE (email) or CONSTRAINT uq_email UNIQUE (email)
id INT PRIMARY KEYPRIMARY KEY (id), needed for multi-column keys
fee DECIMAL(8,2) CHECK (fee >= 0)CONSTRAINT chk_fee CHECK (fee >= 0), can use several columns
NOT NULL, DEFAULT: column-level only—
Name your constraints with CONSTRAINT name … (for example fk_appt_doctor, chk_fee). Error messages then tell you exactly which rule failed, and you can drop the constraint by name later.

5. Listing a Table's Constraints

query.sql
SELECT constraint_name, constraint_type
FROM information_schema.table_constraints
WHERE table_schema = 'hospital' AND table_name = 'appointments'
ORDER BY constraint_type, constraint_name;

Result: 5 rows

CONSTRAINT_NAMECONSTRAINT_TYPE
chk_feeCHECK
chk_statusCHECK
fk_appt_doctorFOREIGN KEY
PRIMARYPRIMARY KEY
uq_slotUNIQUE

CHECK constraints have their own view with the condition text:

query.sql
SELECT constraint_name, check_clause
FROM information_schema.check_constraints
WHERE constraint_schema = 'hospital';

Result: 2 rows

CONSTRAINT_NAMECHECK_CLAUSE
chk_fee(`fee` >= 0)
chk_status(`status` in (_utf8mb4\'booked\',_utf8mb4\'done\',_utf8mb4\'cancelled\'))

6. Adding and Dropping Constraints Later

Use ALTER TABLE. Existing rows must already satisfy a new constraint, or it is refused:

add-fails.sql
INSERT INTO appointments (patient, doctor_id, appt_date, fee) VALUES ('Chen', 2, '2026-03-12', 50);
ALTER TABLE appointments ADD CONSTRAINT chk_min_fee CHECK (fee >= 100);
Output
Query OK, 1 row affected
ERROR 3819 (HY000): Check constraint 'chk_min_fee' is violated.
add-works.sql
UPDATE appointments SET fee = 100 WHERE fee < 100;
ALTER TABLE appointments ADD CONSTRAINT chk_min_fee CHECK (fee >= 100);
Output
Query OK, 1 row affected
Rows matched: 1  Changed: 1  Warnings: 0
Query OK, 2 rows affected
Records: 2  Duplicates: 0  Warnings: 0
ConstraintAddDrop
PRIMARY KEYADD PRIMARY KEY (col)DROP PRIMARY KEY
UNIQUEADD CONSTRAINT uq UNIQUE (col)DROP INDEX uq
FOREIGN KEYADD CONSTRAINT fk FOREIGN KEY (col) REFERENCES t(col)DROP FOREIGN KEY fk
CHECKADD CONSTRAINT chk CHECK (expr)DROP CHECK chk (or DROP CONSTRAINT chk)
NOT NULLMODIFY col type NOT NULLMODIFY col type NULL
DEFAULTALTER COLUMN col SET DEFAULT vALTER COLUMN col DROP DEFAULT
query.sql
ALTER TABLE appointments DROP CONSTRAINT chk_min_fee;
Output
Query OK, 0 rows affected
Records: 0  Duplicates: 0  Warnings: 0

7. Constraints vs Validation in Your Application

Java applications usually validate input too (for example with Bean Validation's @NotNull and @Size). That's good for friendly messages, but it doesn't replace database constraints: other programs, scripts and bugs can bypass application code. Use both: validation for the user, constraints as the last line of defence.

8. Practice Exercises

Exercise 1: Add a rule that appointment dates must be in 2026 or later.
solution.sql
ALTER TABLE appointments ADD CONSTRAINT chk_year CHECK (appt_date >= '2026-01-01');
INSERT INTO appointments (patient, doctor_id, appt_date) VALUES ('Dev', 1, '2025-12-31');
Output
Query OK, 2 rows affected
Records: 2  Duplicates: 0  Warnings: 0
ERROR 3819 (HY000): Check constraint 'chk_year' is violated.
Exercise 2: List all constraints in the hospital database with their table and type.
solution.sql
SELECT table_name, constraint_name, constraint_type
FROM information_schema.table_constraints
WHERE table_schema = 'hospital'
ORDER BY table_name, constraint_type;

Result: 8 rows

TABLE_NAMECONSTRAINT_NAMECONSTRAINT_TYPE
appointmentschk_feeCHECK
appointmentschk_statusCHECK
appointmentschk_yearCHECK
appointmentsfk_appt_doctorFOREIGN KEY
appointmentsPRIMARYPRIMARY KEY
appointmentsuq_slotUNIQUE
doctorsPRIMARYPRIMARY KEY
doctorsreg_noUNIQUE

9. Summary

  • Constraints are rules MySQL enforces on every write: NOT NULL, UNIQUE, PRIMARY KEY, FOREIGN KEY, CHECK, and DEFAULT.
  • Define them at column level or table level; name them for clear errors.
  • Add or drop them later with ALTER TABLE; existing data must already comply.
  • List them in information_schema.table_constraints and check_constraints.
  • Keep application validation too, but let the database have the final say.