Brightlane's customer success team wants a global coverage summary describing how customer and order records pair together.
Write a query to return the total number of customer-order pairings and the number of actual order records as a single row.
Assumptions:
- Every customer must contribute to the pairing count, including customers with no orders. Customers with no orders contribute a single pairing each with a missing order ID.
- The pairing count includes every pairing record; the order count includes only pairings where an actual order is present.
Output:
- A single row with columns
customer_order_rowsandorder_count.
Schema · ecommerce 5 tables
Run previews · Check grades
Write a query, then run it to see results here.
Worked solution Try it yourself first
SELECT
COUNT(*) AS customer_order_rows,
COUNT(o.id) AS order_count
FROM
customers c
LEFT JOIN orders o ON c.id = o.customer_id The shape
COUNT(*) counts every row the LEFT JOIN produces, including the placeholder rows for customers with no orders. COUNT(o.id) counts only the rows where an actual order was attached, because COUNT(column) skips missing values by design. The gap between the two figures is exactly the number of customers the LEFT JOIN is preserving.
Clause by clause
SELECT COUNT(*) AS customer_order_rows, COUNT(o.id) AS order_countreturns the two figures as a single row.COUNT(*)returns208— the full set of join output rows.COUNT(o.id)returns200— only the rows where the join actually attached an order. The difference,8, equals the number of customers with no orders at all (the same eight surfaced by an anti-join).FROM customers c LEFT JOIN orders o ON c.id = o.customer_idpairs each customer with their orders, preserving customers who have none by emitting a single row with everyo.* column missing.
Why COUNT(o.id) and not COUNT(o.status) or COUNT(o.total_amount)
Any non-missing column on a real order would give the same result here, because every real order has every column recorded. But the safe column to count is the primary key, o.id. A primary key is guaranteed non-missing on real rows and guaranteed missing on the LEFT JOIN-introduced placeholders, so the count cleanly separates the two populations. Counting a column that can legitimately be missing on a real order would undercount.
The trap
COUNT(o.id) and COUNT(*) look interchangeable until the LEFT JOIN introduces missing values. After that, they answer different questions: COUNT(*) asks how many rows came out of the join, and COUNT(o.id) asks how many of those rows have an actual order attached. The two numbers only match when there are no unmatched rows.
You practiced COUNT(*) vs COUNT(right.id) after a LEFT JOIN — the first includes the unmatched-row placeholders, the second skips them.