Hook: Interviewers love this because a composite key checks whether you know how to identify a row by a pair of columns, not just a single ID.
Question: What is a composite key in SQL?
Answer: A composite key is a key made from two or more columns that together uniquely identify a row. One column by itself is not enough; the combination is what matters. In SQL, you usually enforce it with a composite PRIMARY KEY or UNIQUE constraint.
Interview-Ready Answer: A composite key is a row identifier made from more than one column. I use it when no single column is unique enough, like order_id plus product_id in an order line table. In SQL, I would enforce that with a composite primary key so the database prevents duplicate pairs and can index the pair for fast lookups.
Detailed Explanation: A composite key means the database treats multiple columns as one logical identifier. Think of it as an address made from several parts: each part helps, but only the full combination points to one row. Most interview questions are really testing whether you understand that the uniqueness rule applies to the set of columns, not each column alone.
PRIMARY KEY, every column is also treated as NOT NULL, so the key must be fully present.(order_id, product_id), then WHERE order_id = 1001 can use the index well, but WHERE product_id = 501 usually cannot use it efficiently by itself.Use a composite key when the real-world identity naturally comes from more than one value: line items in an order, a student in a course, or language-specific translations. It is especially strong in junction tables, which are tables that connect two other tables in many-to-many relationships.
| Aspect | Composite Key | Surrogate Key |
|---|---|---|
| Meaning | Business columns | Artificial ID |
| Example | (order_id, product_id) | line_item_id |
| Readability | High | Lower |
| Storage | Wider index | Usually smaller |
| Stability | Poor if fields change | Very stable |
A surrogate key is a made-up ID with no business meaning. It is often easier to reference from many tables, but it adds another column and does not prevent duplicate business data unless you also add a composite UNIQUE constraint.
O(log n), because the engine searches a tree, not the whole table.INT columns might be around 8 bytes of raw data, while two long VARCHAR values can expand the index to hundreds of bytes per entry.PRIMARY KEY cannot contain NULL values.UNIQUE constraint is similar, but some databases treat NULL values differently there.Memory Hook: Think of a concert seat: section + row + seat. Any one part alone is incomplete, but the full combination finds exactly one seat.
Real-World Story: In a checkout service, each order can contain many products. The table that stores line items often uses (order_id, product_id) as the composite key, because one order may contain product 501 and product 502, but the same product should not be duplicated in the same order line table.
Imagine the team accidentally uses only order_id as the primary key. The first item inserts fine, but the second item for the same order fails with a duplicate-key error. In production, that shows up as missing items in receipts, incorrect totals, and logs like duplicate key value violates unique constraint. If the app retries blindly, customers may see half-built carts or shipment rows that point to the wrong line item.
The same idea helps in shipping. A shipment table may need to reference both the order and the specific product line, so a composite foreign key prevents orphan shipment rows from pointing at a line item that does not exist. That is the practical value: the database protects the business rule for you.
-- Demonstration of a composite key in a realistic order system.
-- This script is written for PostgreSQL-style SQL and should run as-is.
-- It shows both the main idea and a failure path via commented lines.
DROP TABLE IF EXISTS shipment_items;
DROP TABLE IF EXISTS order_items;
DROP TABLE IF EXISTS orders;
CREATE TABLE orders (
order_id INTEGER PRIMARY KEY,
customer_name VARCHAR(100) NOT NULL
);
CREATE TABLE order_items (
order_id INTEGER NOT NULL,
product_id INTEGER NOT NULL,
quantity INTEGER NOT NULL CHECK (quantity > 0),
unit_price DECIMAL(10,2) NOT NULL CHECK (unit_price >= 0),
-- The pair is the identity of a line item.
PRIMARY KEY (order_id, product_id),
-- A child table can still reference just the parent order.
FOREIGN KEY (order_id) REFERENCES orders(order_id)
);
CREATE TABLE shipment_items (
shipment_id INTEGER NOT NULL,
order_id INTEGER NOT NULL,
product_id INTEGER NOT NULL,
shipped_qty INTEGER NOT NULL CHECK (shipped_qty > 0),
PRIMARY KEY (shipment_id, order_id, product_id),
-- This composite foreign key guarantees the shipment points at a real line item.
FOREIGN KEY (order_id, product_id)
REFERENCES order_items(order_id, product_id)
);
INSERT INTO orders (order_id, customer_name) VALUES
(1001, 'Ava'),
(1002, 'Noah');
INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES
(1001, 501, 2, 19.99),
(1001, 502, 1, 5.50),
(1002, 501, 3, 19.99);
INSERT INTO shipment_items (shipment_id, order_id, product_id, shipped_qty) VALUES
(9001, 1001, 501, 2),
(9001, 1001, 502, 1);
-- Failure path: this duplicate pair would violate the composite primary key.
-- Uncomment to see the error: duplicate key value on (1001, 501).
-- INSERT INTO order_items (order_id, product_id, quantity, unit_price)
-- VALUES (1001, 501, 1, 19.99);
-- Failure path: this row would point to a non-existent order line.
-- Uncomment to see the foreign key error.
-- INSERT INTO shipment_items (shipment_id, order_id, product_id, shipped_qty)
-- VALUES (9002, 1002, 999, 1);
SELECT
oi.order_id,
oi.product_id,
o.customer_name,
oi.quantity,
oi.unit_price
FROM order_items oi
JOIN orders o
ON o.order_id = oi.order_id
ORDER BY oi.order_id, oi.product_id;Follow-up & Tricky Questions:
UNIQUE constraint if the business rule depends on multiple columns.NULL? Not if it is a primary key. Primary key columns are implicitly NOT NULL.(col1, col2). The engine typically needs the leftmost column to use that index well.Common Mistakes:
UNIQUE constraints for other business rules.Memory Hook: section + row + seat: one part alone is not enough, but the full combination points to exactly one place.
Cheat Sheet:
NOT NULL.Practice Tasks:
student_courses table using (student_id, course_id) as the primary key.