Chapter 18 · Capstone: Design and Query a Complete Database
Write Operational and Analytical Queries
A complete database must support both precise day-to-day decisions and trustworthy analysis. You will write queries for order service, fulfillment, inventory, payments, customer value, product performance, regional revenue, and management reporting without losing control of result grain.
Learning outcomes
Learning outcomes
State the grain of each result before writing joins and aggregates.
Build operational queries for customer service, order detail, fulfillment, payments, inventory, and stable pagination.
Build analytical queries with CTEs, conditional aggregation, date grouping, and window functions.
Prevent join fan-out by pre-aggregating each many-side to the required grain.
Turn metric definitions into readable, testable SQL contracts.
One database, two query workloads
Operational query
Returns a small, current, precisely filtered result for an immediate action. Latency and deterministic lookup matter.
Analytical query
Scans and summarizes a larger history to answer a business question. Grain and metric definitions matter.
Result grain
The sentence “one row represents …” controls joins, grouping columns, uniqueness, and interpretation.
Evidence
Expected columns, sample outputs, row-count bounds, reconciliation totals, and plans make a query reviewable.
Write the result grain before the SELECT list.
Operational lookup: complete order view
SELECT so.order_number, so.status AS order_status, so.ordered_at, c.display_name AS customer_name, a.label AS address_label, a.city AS ship_city, oi.line_no, p.sku, p.product_name, oi.quantity, oi.unit_price_cents, oi.line_total_centsFROM sales_order AS soJOIN customer AS c ON c.customer_id = so.customer_idJOIN address AS a ON a.address_id = so.shipping_address_idJOIN order_item AS oi ON oi.order_id = so.order_idJOIN product AS p ON p.product_id = oi.product_idWHERE so.order_number = :order_numberORDER BY oi.line_no;The grain is one row per order line. Customer, address, and order attributes repeat intentionally because they describe each line’s parent context.
Operational summary without fan-out
WITH line_totals AS ( SELECT order_id, COUNT(*) AS line_count, SUM(quantity) AS unit_count, SUM(line_total_cents) AS order_total_cents FROM order_item GROUP BY order_id), payment_totals AS ( SELECT order_id, SUM(CASE WHEN status = 'captured' THEN amount_cents ELSE 0 END) AS captured_cents, SUM(CASE WHEN status = 'refunded' THEN amount_cents ELSE 0 END) AS refunded_cents FROM payment GROUP BY order_id), shipment_counts AS ( SELECT order_id, COUNT(*) AS shipment_count FROM shipment GROUP BY order_id), latest_shipment AS ( SELECT s.order_id, s.status AS latest_shipment_state FROM shipment s JOIN ( SELECT order_id, MAX(shipment_id) AS shipment_id FROM shipment GROUP BY order_id ) latest ON latest.order_id = s.order_id AND latest.shipment_id = s.shipment_id)SELECT so.order_number, so.status, lt.line_count, lt.unit_count, lt.order_total_cents, COALESCE(pt.captured_cents,0) AS captured_cents, COALESCE(pt.refunded_cents,0) AS refunded_cents, COALESCE(sc.shipment_count,0) AS shipment_count, ls.latest_shipment_stateFROM sales_order soJOIN line_totals lt ON lt.order_id = so.order_idLEFT JOIN payment_totals pt ON pt.order_id = so.order_idLEFT JOIN shipment_counts sc ON sc.order_id = so.order_idLEFT JOIN latest_shipment ls ON ls.order_id = so.order_idORDER BY so.ordered_at, so.order_id;Each many-side is reduced to one row per order before joining. Directly joining order items, payments, and shipments would multiply rows and corrupt sums.
Fulfillment queue and available inventory
SELECT so.order_number, so.ordered_at, w.warehouse_code, p.sku, oi.quantity AS required_quantity, i.on_hand - i.reserved AS available_quantity, CASE WHEN i.on_hand - i.reserved >= oi.quantity THEN 'ready' ELSE 'short' END AS allocation_stateFROM sales_order soJOIN order_item oi ON oi.order_id = so.order_idJOIN product p ON p.product_id = oi.product_idCROSS JOIN warehouse wLEFT JOIN inventory i ON i.warehouse_id = w.warehouse_id AND i.product_id = oi.product_idWHERE so.status IN ('submitted','paid')ORDER BY so.ordered_at, so.order_id, oi.line_no, w.warehouse_id;The result grain is one candidate warehouse per order line. The final reservation transaction must choose one warehouse and re-check availability atomically.
Payment reconciliation
WITH order_totals AS ( SELECT order_id, SUM(line_total_cents) AS order_total_cents FROM order_item GROUP BY order_id), money AS ( SELECT order_id, SUM(CASE WHEN status='captured' THEN amount_cents ELSE 0 END) AS captured_cents, SUM(CASE WHEN status='refunded' THEN amount_cents ELSE 0 END) AS refunded_cents FROM payment GROUP BY order_id)SELECT so.order_number, so.status, ot.order_total_cents, COALESCE(m.captured_cents,0) AS captured_cents, COALESCE(m.refunded_cents,0) AS refunded_cents, COALESCE(m.captured_cents,0) - COALESCE(m.refunded_cents,0) AS net_collected_cents, CASE WHEN so.status='cancelled' AND COALESCE(m.refunded_cents,0)=0 THEN 'refund review' WHEN so.status IN ('paid','packed','shipped') AND COALESCE(m.captured_cents,0)<>ot.order_total_cents THEN 'amount mismatch' ELSE 'ok' END AS reconciliation_stateFROM sales_order soJOIN order_totals ot ON ot.order_id=so.order_idLEFT JOIN money m ON m.order_id=so.order_idORDER BY reconciliation_state DESC, so.order_number;Stable operational pagination
SELECT order_id, order_number, customer_id, status, ordered_atFROM sales_orderWHERE (ordered_at, order_id) < (:last_ordered_at, :last_order_id)ORDER BY ordered_at DESC, order_id DESCLIMIT :page_size;| Property | Design consequence |
|---|---|
| Determinism | The unique order_id tie-breaker prevents unstable ordering when timestamps match. |
| Continuation | The next request carries the last ordered_at and order_id from the previous page. |
| Concurrency | Newer inserted rows do not shift earlier pages as OFFSET pagination can. |
| Index shape | A composite index on ordered_at DESC, order_id DESC supports the traversal. |
Recognized monthly revenue by region
WITH recognized AS ( SELECT so.order_id, so.customer_id, substr(so.ordered_at,1,7) AS order_month, SUM(CASE WHEN p.status='captured' THEN p.amount_cents WHEN p.status='refunded' THEN -p.amount_cents ELSE 0 END) AS net_revenue_cents FROM sales_order so JOIN payment p ON p.order_id=so.order_id WHERE so.status <> 'cancelled' GROUP BY so.order_id, so.customer_id, substr(so.ordered_at,1,7))SELECT r.order_month, c.region, COUNT(DISTINCT r.order_id) AS orders, COUNT(DISTINCT r.customer_id) AS customers, SUM(r.net_revenue_cents) AS net_revenue_centsFROM recognized rJOIN customer c ON c.customer_id=r.customer_idGROUP BY r.order_month, c.regionORDER BY r.order_month, c.region;The metric contract states that recognized revenue is captured minus refunded payment for non-cancelled orders. Altering that definition requires a reviewed metric change, not an ad hoc report edit.
Product performance and share
WITH product_revenue AS ( SELECT p.product_id, p.sku, p.product_name, SUM(CASE WHEN so.status <> 'cancelled' THEN oi.quantity ELSE 0 END) AS units, SUM(CASE WHEN so.status <> 'cancelled' THEN oi.line_total_cents ELSE 0 END) AS gross_cents FROM product p LEFT JOIN order_item oi ON oi.product_id=p.product_id LEFT JOIN sales_order so ON so.order_id=oi.order_id GROUP BY p.product_id, p.sku, p.product_name)SELECT sku, product_name, units, gross_cents, ROUND(100.0 * gross_cents / NULLIF(SUM(gross_cents) OVER (),0),2) AS gross_share_pct, DENSE_RANK() OVER (ORDER BY gross_cents DESC) AS revenue_rankFROM product_revenueORDER BY revenue_rank, sku;Customer lifetime value and repeat behavior
WITH customer_orders AS ( SELECT so.customer_id, so.order_id, so.ordered_at, SUM(oi.line_total_cents) AS order_total_cents FROM sales_order so JOIN order_item oi ON oi.order_id=so.order_id WHERE so.status <> 'cancelled' GROUP BY so.customer_id, so.order_id, so.ordered_at)SELECT c.customer_id, c.display_name, COUNT(co.order_id) AS completed_or_open_orders, COALESCE(SUM(co.order_total_cents),0) AS lifetime_order_value_cents, MIN(co.ordered_at) AS first_order_at, MAX(co.ordered_at) AS latest_order_at, CASE WHEN COUNT(co.order_id) >= 2 THEN 'repeat' ELSE 'single/none' END AS customer_segmentFROM customer cLEFT JOIN customer_orders co ON co.customer_id=c.customer_idGROUP BY c.customer_id, c.display_nameORDER BY lifetime_order_value_cents DESC, c.customer_id;Inventory risk report
SELECT w.warehouse_code, p.sku, p.product_name, i.on_hand, i.reserved, i.on_hand - i.reserved AS available, i.reorder_point, CASE WHEN i.on_hand - i.reserved <= 0 THEN 'stockout' WHEN i.on_hand - i.reserved <= i.reorder_point THEN 'reorder' ELSE 'healthy' END AS stock_stateFROM inventory iJOIN warehouse w ON w.warehouse_id=i.warehouse_idJOIN product p ON p.product_id=i.product_idWHERE p.active=1ORDER BY CASE WHEN i.on_hand - i.reserved <= 0 THEN 1 WHEN i.on_hand - i.reserved <= i.reorder_point THEN 2 ELSE 3 END, available, w.warehouse_code, p.sku;Query validation matrix
| Query | Primary grain | Must reconcile with |
|---|---|---|
| Order detail | one order line | SUM(line_total_cents) equals order total |
| Order summary | one order | No fan-out when payments or shipments multiply |
| Fulfillment candidates | one warehouse per order line | Availability equals on_hand minus reserved |
| Monthly revenue | month + region | Sum equals the same metric without regional grouping |
| Product performance | one product | Total gross equals non-cancelled line total |
| Customer value | one customer | Customer totals equal non-cancelled order totals |
| Inventory risk | one warehouse-product balance | Every active inventory row classified exactly once |
Query review
- Why are order items, payments, and shipments pre-aggregated before the order summary join?
- Why does keyset pagination include order_id after ordered_at?
- Why is a metric definition part of the query contract?
- What reconciliation test can detect accidental join fan-out?
Review the answers
Pre-aggregation aligns every many-side to one row per order. The unique tie-breaker makes ordering total and continuation unambiguous. Metric definitions prevent reports from assigning different meaning to the same label. Compare grouped totals with a simpler control total and verify expected row counts.
Lesson summary
- Operational queries optimize for precise action; analytical queries optimize for trustworthy interpretation.
- Result grain must be explicit before joins and grouping.
- Pre-aggregate multiple child sets before joining them to avoid fan-out.
- Stable pagination requires a deterministic order and continuation key.
- Every important report should have reconciliation totals and testable metric definitions.