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.

Capstone185–225 minutesOperational SQL + analytical reporting laboratoryLast reviewed: August 2026

Learning outcomes

Learning outcomes

01

State the grain of each result before writing joins and aggregates.

02

Build operational queries for customer service, order detail, fulfillment, payments, inventory, and stable pagination.

03

Build analytical queries with CTEs, conditional aggregation, date grouping, and window functions.

04

Prevent join fan-out by pre-aggregating each many-side to the required grain.

05

Turn metric definitions into readable, testable SQL contracts.

One database, two query workloads

OPS

Operational query

Returns a small, current, precisely filtered result for an immediate action. Latency and deterministic lookup matter.

ANA

Analytical query

Scans and summarizes a larger history to answer a business question. Grain and metric definitions matter.

GRA

Result grain

The sentence “one row represents …” controls joins, grouping columns, uniqueness, and interpretation.

EVD

Evidence

Expected columns, sample outputs, row-count bounds, reconciliation totals, and plans make a query reviewable.

Question
Required grain
Source grains
Join strategy
Filter semantics
Validation

Write the result grain before the SELECT list.

Operational lookup: complete order view

sql · one row per order line
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

sql · one row per order
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

sql · actionable submitted orders
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

sql · exceptions first
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

sql · keyset page
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;
PropertyDesign consequence
DeterminismThe unique order_id tie-breaker prevents unstable ordering when timestamps match.
ContinuationThe next request carries the last ordered_at and order_id from the previous page.
ConcurrencyNewer inserted rows do not shift earlier pages as OFFSET pagination can.
Index shapeA composite index on ordered_at DESC, order_id DESC supports the traversal.

Recognized monthly revenue by region

sql · management metric
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

sql · aggregate plus window function
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

sql · one row per customer
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

sql · stock policy
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

QueryPrimary grainMust reconcile with
Order detailone order lineSUM(line_total_cents) equals order total
Order summaryone orderNo fan-out when payments or shipments multiply
Fulfillment candidatesone warehouse per order lineAvailability equals on_hand minus reserved
Monthly revenuemonth + regionSum equals the same metric without regional grouping
Product performanceone productTotal gross equals non-cancelled line total
Customer valueone customerCustomer totals equal non-cancelled order totals
Inventory riskone warehouse-product balanceEvery active inventory row classified exactly once

Query review

  1. Why are order items, payments, and shipments pre-aggregated before the order summary join?
  2. Why does keyset pagination include order_id after ordered_at?
  3. Why is a metric definition part of the query contract?
  4. 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.

References

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only assets compatible with the Ethereum/ERC-20 network. Do not send TRC-20/TRON assets.