Hook: Interviewers love this because it tests the tiny but important difference between unique and minimal unique.
Question: What is a candidate key in SQL?
Answer: A candidate key is the smallest set of column(s) that can uniquely identify one row in a table. Smallest means you cannot remove any column without losing uniqueness. SQL does not have a special CANDIDATE KEY clause; you usually express it with PRIMARY KEY or UNIQUE plus NOT NULL.
Interview-Ready Answer: A candidate key is any minimal set of columns that uniquely identifies a row. In plain SQL, I do not declare a candidate key directly; I enforce it with a PRIMARY KEY or a UNIQUE constraint, and I make sure the columns are NOT NULL. If a table has more than one candidate key, I choose one as the primary key and keep the others as alternate keys.
In the relational model, a candidate key is the smallest set of columns that still uniquely identifies each row. Unique means no two rows can share the same values in that set. Smallest means if you remove any one column, the uniqueness guarantee breaks.
Think of it like an ID card: if the columns together are enough to point to one person, but no smaller subset works, you have a candidate key. A table can have more than one candidate key, which is why the word candidate matters: several options are eligible, and the database designer chooses one as the primary key.
| Term | Meaning | Extra columns? | SQL note |
|---|---|---|---|
| Candidate key | Minimal unique identifier | No | Model with PRIMARY KEY or UNIQUE + NOT NULL |
| Superkey | Any unique identifier | Yes, allowed | May be unique but not minimal |
| Primary key | Chosen candidate key | No | One per table |
| Alternate key | Unused candidate key | No | Usually UNIQUE + NOT NULL |
SQL does not have a separate CANDIDATE KEY keyword. You model candidate keys with constraints: PRIMARY KEY for the chosen one, and UNIQUE plus NOT NULL for the others. That distinction matters because UNIQUE alone is not always enough: NULL handling differs by database, and a candidate key in the relational sense cannot contain NULL at all.
Enforcing a candidate key usually means creating a unique index. That adds space overhead, often O(n) for the index, and makes inserts and updates a little slower because the database must check uniqueness, usually with an index lookup of about O(log n). On a large table, a B-tree index might be only 3 to 4 levels deep for millions of rows, so the check is still fast, but it is not free.
There is also a design trade-off between natural and surrogate keys. A natural key is a real-world value such as email or national_id; a surrogate key is a made-up identifier such as an integer id. Natural candidate keys are easy for humans to understand, but they can change and may be wider. Surrogate keys are stable and narrow, but you still often keep the natural candidate keys as UNIQUE constraints so the business rule is enforced.
(order_id, line_no). That is common in join tables and line-item tables.(customer_id, email) may be unique, but if customer_id alone is enough, then the pair is only a superkey, not a candidate key.Imagine an e-commerce checkout service. Each customer has a system id, an email address, and a national_id from a verified sign-up step. The team correctly uses customer_id as the primary key, but they also keep email and national_id as candidate keys because each one should identify exactly one customer.
One day, a developer builds a merge job that matches accounts using full_name and phone number instead of a real candidate key. It works in testing, then fails in production when two different people share the same name and change phone numbers. The symptoms are messy: duplicate customer records, orders linked to the wrong account, refund emails sent to the wrong person, and logs full of foreign key errors when the cleanup job tries to rewire rows.
The business impact is serious: support tickets spike, loyalty points split across accounts, and finance cannot trust the join between payments and customers. The fix is to identify the true candidate keys from the business rules, enforce them with UNIQUE constraints, and never use a non-key field as the identity of a person just because it looks convenient.
-- Candidate key demo in standard SQL style.
-- We use PRIMARY KEY for one chosen candidate key and UNIQUE for other candidate keys.
CREATE TABLE customers (
customer_id INTEGER NOT NULL,
email VARCHAR(255) NOT NULL,
national_id VARCHAR(20) NOT NULL,
full_name VARCHAR(100) NOT NULL,
CONSTRAINT pk_customers PRIMARY KEY (customer_id),
CONSTRAINT uq_customers_email UNIQUE (email),
CONSTRAINT uq_customers_national_id UNIQUE (national_id)
);
CREATE TABLE order_items (
order_id INTEGER NOT NULL,
line_no INTEGER NOT NULL,
product_sku VARCHAR(30) NOT NULL,
quantity INTEGER NOT NULL,
CONSTRAINT pk_order_items PRIMARY KEY (order_id, line_no)
);
INSERT INTO customers (customer_id, email, national_id, full_name) VALUES
(1, 'maria1@example.com', 'NID-100', 'Maria Chen'),
(2, 'maria2@example.com', 'NID-101', 'Maria Chen');
-- Same full_name is allowed because name is not a candidate key here.
-- The unique identifiers are customer_id, email, and national_id.
INSERT INTO order_items (order_id, line_no, product_sku, quantity) VALUES
(5001, 1, 'SKU-RED-01', 2),
(5001, 2, 'SKU-BLUE-07', 1),
(5002, 1, 'SKU-GREEN-03', 4);
SELECT customer_id, email, national_id, full_name
FROM customers
ORDER BY customer_id;
SELECT order_id, line_no, product_sku, quantity
FROM order_items
ORDER BY order_id, line_no;
-- Edge case / failure path:
-- This would fail in a real database because email is UNIQUE.
-- INSERT INTO customers (customer_id, email, national_id, full_name)
-- VALUES (3, 'maria1@example.com', 'NID-102', 'Maria Clone');
-- This would also fail because the composite key (order_id, line_no) must stay unique.
-- INSERT INTO order_items (order_id, line_no, product_sku, quantity)
-- VALUES (5001, 1, 'SKU-YELLOW-09', 1);Follow-up & Tricky Questions:
customer_id, email, and national_id as separate candidate keys if each one is unique and minimal.(order_id, line_no). This is common when one column by itself is not enough to identify a row.PRIMARY KEY for the chosen key and UNIQUE with NOT NULL for the other candidate keys. That is how you turn the design idea into a database rule.UNIQUE plus NOT NULL.Common Mistakes:
Memory Hook: Think of a candidate key as the smallest keyring that still opens exactly one door. If you can remove a key and still open the same door, it was not a candidate key yet.
Cheat Sheet:
PRIMARY KEY or UNIQUE + NOT NULL.Practice Tasks:
customers table and list every possible candidate key from the business rules.order_items table where (order_id, line_no) is the primary key, then explain why line_no alone is not enough.