A report needs each order with the number of line items it contains. Your query returns a wildly inflated count. Why, and what is the fix?
The inflation comes from the join fanning out before the count happens. One order with three line items becomes three rows after the join, and counting rows in that result counts the same order three times. The order is not duplicated in the database — your query duplicated it.
The fix is to count a column that belongs to the many side, and to group by a key from the one side:
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
customer TEXT NOT NULL,
placed_on TEXT NOT NULL
);
CREATE TABLE order_items (
id INTEGER PRIMARY KEY,
order_id INTEGER NOT NULL,
product TEXT NOT NULL,
quantity INTEGER NOT NULL
);
INSERT INTO orders (id, customer, placed_on) VALUES
(1, 'Ada', '2024-01-05'),
(2, 'Grace', '2024-01-06');
INSERT INTO order_items (id, order_id, product, quantity) VALUES
(1, 1, 'keyboard', 1),
(2, 1, 'mouse', 2),
(3, 1, 'monitor', 1),
(4, 2, 'laptop', 1);
SELECT o.id, o.customer, COUNT(i.id) AS item_count
FROM orders o
LEFT JOIN order_items i ON i.order_id = o.id
GROUP BY o.id, o.customer;Order 1 comes back with a count of 3, order 2 with 1. Note the join is a LEFT JOIN, so an order with no items still appears — with a count of 0 rather than disappearing from the report. If you use an inner join instead, order-less orders vanish silently, which is a different bug with the same symptom of missing rows.
Two details worth raising in an interview. First, count i.id, not Second, the same fan-out ruins SUM and AVG — a sum of order totals over a joined line-item table multiplies every total by the number of items. If you need both the order and its item total, aggregate the items in a subquery first and join the pre-aggregated result.COUNT(*): COUNT(*) counts rows and would return 1 for an order with no items, because the LEFT JOIN still produces one row. COUNT on a nullable column skips the null and correctly returns 0.