SQL Practice Problem

Customers who never ordered

Medium Joins

Schema and sample data

Customers
customer_idname
1Alice Chen
2Bruno Diaz
3Chloe Park
4Dev Patel
5Elena Ruiz
Orders
order_idcustomer_id
1011
1021
1033
1045
Show setup SQL (copy to run)
CREATE TABLE Customers (
    customer_id INT PRIMARY KEY,
    name        VARCHAR(50) NOT NULL
);

CREATE TABLE Orders (
    order_id    INT PRIMARY KEY,
    customer_id INT NOT NULL
);

INSERT INTO Customers (customer_id, name) VALUES
(1, 'Alice Chen'),
(2, 'Bruno Diaz'),
(3, 'Chloe Park'),
(4, 'Dev Patel'),
(5, 'Elena Ruiz');

INSERT INTO Orders (order_id, customer_id) VALUES
(101, 1),
(102, 1),
(103, 3),
(104, 5);
Your task

You have a Customers table and an Orders table. Return the name of every customer who has never placed an order, that is, a customer with no matching row in Orders. Sort the result by name in ascending order.

Expected result

Your query should return
name
Bruno Diaz
Dev Patel
Show hint

Think about an anti join: keep every customer, attach their orders with a LEFT JOIN, then keep only the rows where the order side came back empty. Which column tells you that no order matched?

Try to write the query yourself before you open this.

Show solution and explanation
SELECT c.name
FROM Customers AS c
LEFT JOIN Orders AS o
    ON o.customer_id = c.customer_id
WHERE o.order_id IS NULL
ORDER BY c.name;

The LEFT JOIN keeps every customer even when no order matches. For customers 1, 3 and 5 the join finds rows in Orders, so o.order_id holds a real value. For customers 2 (Bruno Diaz) and 4 (Dev Patel) there is no match, so the join fills the Orders columns with NULL. The filter WHERE o.order_id IS NULL then keeps exactly those unmatched rows. This shape is called an anti join. See how the join types work for the full picture.

A cleaner and NULL safe alternative is NOT EXISTS, which many people prefer because it says what you mean and stops at the first matching order: SELECT name FROM Customers c WHERE NOT EXISTS (SELECT 1 FROM Orders o WHERE o.customer_id = c.customer_id) ORDER BY name;. It returns the same two customers and reads as a direct question: keep the customer when no order exists for them.

Do not reach for NOT IN with a subquery here. WHERE customer_id NOT IN (SELECT customer_id FROM Orders) looks equivalent, but if the Orders.customer_id list ever contains a NULL, the whole NOT IN evaluates to UNKNOWN and the query returns zero rows. That is the classic NOT IN NULL trap. Prefer NOT EXISTS or the LEFT JOIN ... IS NULL anti join, both of which handle NULLs safely. For a walkthrough of this exact scenario, see the customers with no orders interview question.

Keep practising

Work through more Medium exercises, or test yourself with the SQL interview questions.