Interview question
Avoid double-counting payments and refunds
Pre-aggregates two one-to-many relationships before joining them to prevent fanout from corrupting financial totals.
TL;DR
Pre-aggregates two one-to-many relationships before joining them to prevent fanout from corrupting financial totals.
Join cardinality, pre-aggregation, financial correctness, null totals, and report reconciliation.
Practice the problem like a real interview: restate, reason, implement, and test.
Tables: Orders(OrderId, CreatedAtUtc), Payments(PaymentId, OrderId, Amount, Status), and Refunds(RefundId, OrderId, Amount, Status). For orders in a requested range, return captured payment total, completed refund total, and net paid amount.
An order has two captured payments and three completed refunds. Joining both detail tables directly would create six rows and repeat amounts. The result must sum each detail table once.
I aggregate payments to one row per order and refunds to one row per order before joining either result to Orders. Once both child sets have the same grain as the parent, their totals cannot multiply each other. I use COALESCE only after the joins so missing child groups become zero.
WITH PaymentTotals AS
(
SELECT p.OrderId, SUM(p.Amount) AS PaidAmount
FROM dbo.Payments AS p
WHERE p.Status = 'Captured'
GROUP BY p.OrderId
),
RefundTotals AS
(
SELECT r.OrderId, SUM(r.Amount) AS RefundedAmount
FROM dbo.Refunds AS r
WHERE r.Status = 'Completed'
GROUP BY r.OrderId
)
SELECT
o.OrderId,
COALESCE(p.PaidAmount, CONVERT(decimal(18, 2), 0)) AS PaidAmount,
COALESCE(r.RefundedAmount, CONVERT(decimal(18, 2), 0)) AS RefundedAmount,
COALESCE(p.PaidAmount, CONVERT(decimal(18, 2), 0))
- COALESCE(r.RefundedAmount, CONVERT(decimal(18, 2), 0)) AS NetPaidAmount
FROM dbo.Orders AS o
LEFT JOIN PaymentTotals AS p ON p.OrderId = o.OrderId
LEFT JOIN RefundTotals AS r ON r.OrderId = o.OrderId
WHERE o.CreatedAtUtc >= @FromUtc
AND o.CreatedAtUtc < @ToUtc
ORDER BY o.OrderId;
Each child table is scanned or sought once and reduced to one row per order before the final joins. On large tables, filter child aggregates to the relevant order set or provide indexes on (OrderId, Status) including Amount so the report does not aggregate unrelated history.
DISTINCT on monetary values and losing legitimate equal payments.Move to the linked follow-up, next path step, prerequisite, or deeper variant.
Practice the next layer of the same subject.