Brightlane's fulfilment team wants a line-item breakdown for delivered orders only, showing what products were included and at what price.
Write a query to return the customer name, product name, and unit price for every item in a delivered order.
Assumptions:
- The chain reaches:
orders→customers,orders→order_items→products. - A delivered order has
status = 'delivered'; the condition applies to the order side of the chain and removes line items belonging to non-delivered orders.
Output:
- One row per qualifying line item, with columns
customer_name,product_name, andunit_price.
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
c.name AS customer_name,
p.name AS product_name,
oi.unit_price
FROM
orders o
JOIN customers c ON o.customer_id = c.id
JOIN order_items oi ON o.id = oi.order_id
JOIN products p ON oi.product_id = p.id
WHERE
o.status = 'delivered' The shape
The same three-table chain as the easies, with one WHERE predicate on the central table narrowing the entire result. o.status = 'delivered' runs against orders and drops every line item belonging to a non-delivered order — line items inherit their order's status by virtue of being chained to it.
Clause by clause
SELECT c.name AS customer_name, p.name AS product_name, oi.unit_pricepicks one column from each of the three non-junction tables.FROM orders oanchors the chain.JOIN customers c ON o.customer_id = c.idattaches the customer to each order.JOIN order_items oi ON o.id = oi.order_idattaches the line items — the multiplying step.JOIN products p ON oi.product_id = p.idresolves each line item to its product.WHERE o.status = 'delivered'filters the joined result down to rows whose underlying order hasstatus = 'delivered'. The predicate sits on theordersside of the chain, but it shapes the final row set because every line item is tied to one order — drop an order, and every line item attached to it drops with it.
Why this and not putting status in the ON clause
Both shapes return the same rows here, because the chain is all INNER JOINs and an inner join treats ON conditions and WHERE conditions identically. The convention is to put join keys (the fk = pk shape) in the ON clause and row-level filters in the WHERE clause, because that's what each clause is for — ON says how the tables connect, WHERE says which rows survive. Mixing them works for inner joins and breaks for outer joins; getting in the habit now prevents the silent bug later.
You practiced narrowing a multi-table chain by an attribute on the central table (orders.status). A WHERE clause on any single table in the chain narrows the entire result.