MySQL UNIQUE
The UNIQUE constraint makes sure no two rows have the same value in a column, or the same combination of values in a group of columns. Typical uses: usernames, email addresses, phone numbers, PAN or ISBN codes, and "one vote per user per poll".
1. Creating a UNIQUE Column
CREATE TABLE users (
user_id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(30) NOT NULL UNIQUE,
email VARCHAR(80) UNIQUE,
phone VARCHAR(15)
);
INSERT INTO users (username, email) VALUES ('ravi_k', 'ravi@mail.example'), ('sonal', 'sonal@mail.example'), ('tom', NULL);
INSERT INTO users (username, email) VALUES ('ravi_k', 'ravi2@mail.example');
Query OK, 0 rows affected
Query OK, 3 rows affected
Records: 3 Duplicates: 0 Warnings: 0
ERROR 1062 (23000): Duplicate entry 'ravi_k' for key 'users.username'
The error names the key, users.username. MySQL created a unique index for it automatically:
SHOW INDEX FROM users;
Result: 3 rows
| Key_name | Column_name | Non_unique |
|---|---|---|
| PRIMARY | user_id | 0 |
| username | username | 0 |
| 0 |
2. UNIQUE and NULL
NULL means "unknown", and two unknowns aren't considered equal, so a UNIQUE column can hold many NULLs:
INSERT INTO users (username, email) VALUES ('uma', NULL), ('vik', NULL);
SELECT username, email FROM users WHERE email IS NULL;
Result: 3 rows
| username | |
|---|---|
| tom | NULL |
| uma | NULL |
| vik | NULL |
NOT NULL UNIQUE, as with username.3. Case-Insensitive Duplicates
With MySQL's default case-insensitive collation, 'Ravi_K' counts as a duplicate of 'ravi_k'. That's usually what you want for usernames and emails:
INSERT INTO users (username) VALUES ('RAVI_K');
ERROR 1062 (23000): Duplicate entry 'RAVI_K' for key 'users.username'
Trailing spaces are significant in MySQL 8's default collation, but leading/trailing spaces in user input are best trimmed by the application before saving.
4. UNIQUE Across Several Columns
A composite unique key allows repeated values in each column, but not the same combination. Each user may vote once per poll:
CREATE TABLE poll_votes (
vote_id INT AUTO_INCREMENT PRIMARY KEY,
poll_id INT NOT NULL,
user_id INT NOT NULL,
choice CHAR(1) NOT NULL,
CONSTRAINT uq_one_vote UNIQUE (poll_id, user_id)
);
INSERT INTO poll_votes (poll_id, user_id, choice) VALUES (1, 1, 'A'), (1, 2, 'B'), (2, 1, 'C');
INSERT INTO poll_votes (poll_id, user_id, choice) VALUES (1, 1, 'B');
Query OK, 0 rows affected
Query OK, 3 rows affected
Records: 3 Duplicates: 0 Warnings: 0
ERROR 1062 (23000): Duplicate entry '1-1' for key 'poll_votes.uq_one_vote'
5. Using UNIQUE for Insert-or-Update
A unique key lets INSERT … ON DUPLICATE KEY UPDATE change a vote instead of failing:
INSERT INTO poll_votes (poll_id, user_id, choice) VALUES (1, 1, 'B')
ON DUPLICATE KEY UPDATE choice = 'B';
SELECT poll_id, user_id, choice FROM poll_votes ORDER BY poll_id, user_id;
Result: 3 rows
| poll_id | user_id | choice |
|---|---|---|
| 1 | 1 | B |
| 1 | 2 | B |
| 2 | 1 | C |
INSERT IGNORE skips duplicates silently (with a warning) instead.
6. Adding and Dropping UNIQUE
Adding a unique constraint fails if duplicates already exist:
UPDATE users SET phone = '98450 12345' WHERE username IN ('sonal', 'tom');
ALTER TABLE users ADD CONSTRAINT uq_phone UNIQUE (phone);
Query OK, 2 rows affected
Rows matched: 2 Changed: 2 Warnings: 0
ERROR 1062 (23000): Duplicate entry '98450 12345' for key 'users.uq_phone'
SELECT phone, COUNT(*) AS times FROM users
WHERE phone IS NOT NULL GROUP BY phone HAVING COUNT(*) > 1;
Result: 1 row
| phone | times |
|---|---|
| 98450 12345 | 2 |
UPDATE users SET phone = '98450 67890' WHERE username = 'tom';
ALTER TABLE users ADD CONSTRAINT uq_phone UNIQUE (phone);
ALTER TABLE users DROP INDEX uq_phone; -- a UNIQUE constraint is dropped as an index
Query OK, 1 row affected
Rows matched: 1 Changed: 1 Warnings: 0
Query OK, 0 rows affected
Records: 0 Duplicates: 0 Warnings: 0
Query OK, 0 rows affected
Records: 0 Duplicates: 0 Warnings: 0
7. UNIQUE vs PRIMARY KEY
| PRIMARY KEY | UNIQUE | |
|---|---|---|
| How many per table | One | Many |
| NULL allowed | No | Yes (any number) |
| Purpose | Identify the row | Prevent duplicate business values |
| InnoDB storage | Clustered: decides row order | Secondary index |
| Referenced by foreign keys | Usually | Possible |
See also MySQL Unique Index.
8. Practice Exercises
Exercise 1: Create a products table where the SKU is unique and each (brand, model) pair is unique.
CREATE TABLE products (
id INT AUTO_INCREMENT PRIMARY KEY,
sku VARCHAR(20) NOT NULL UNIQUE,
brand VARCHAR(20) NOT NULL,
model VARCHAR(30) NOT NULL,
UNIQUE (brand, model)
);
SHOW INDEX FROM products;
Result: 4 rows
| Key_name | Seq_in_index | Column_name |
|---|---|---|
| PRIMARY | 1 | id |
| sku | 1 | sku |
| brand | 1 | brand |
| brand | 2 | model |
Exercise 2: Which unique constraints exist in the social database?
SELECT table_name, constraint_name
FROM information_schema.table_constraints
WHERE table_schema = 'social' AND constraint_type = 'UNIQUE';
Result: 5 rows
| TABLE_NAME | CONSTRAINT_NAME |
|---|---|
| poll_votes | uq_one_vote |
| products | brand |
| products | sku |
| users | |
| users | username |
9. Summary
UNIQUEprevents duplicate values; MySQL enforces it with a unique index.- Many NULLs are allowed; add NOT NULL when the value is required.
- Composite UNIQUE keys prevent duplicate combinations.
- Default collations make the check case-insensitive.
- Add with
ADD CONSTRAINT … UNIQUE(after removing duplicates); drop withDROP INDEX.