Both Ways

A medium SQL interview practice problem on DataDriven. Write the query and run it against a real database, with instant feedback.

Domain
SQL
Difficulty
medium
Seniority
L4
Asked in
Meta

Problem

Our daily net revenue for the second quarter of 2026 won't tie out, partly because refunds in the transactions table land on whatever day they happen: a purchase is a positive total_amount and a refund a negative one. Take every purchase dated April 1 through June 30, 2026, plus every refund no matter its date, and give each day's net revenue in date order.

Money flows in and money flows back. Reconcile the day.

Tables

transactions

Columns: transaction_id INTEGER, user_id INTEGER, product_id INTEGER, quantity INTEGER, total_amount REAL, transaction_date TEXT

transactions: 10 of 200 rows
transaction_iduser_idproduct_idquantitytotal_amounttransaction_date
10671971001223.462026-02-02
11342941050336.932026-03-03
12013911099450.42026-04-04
12684881148563.872026-05-05
13355851197002026-06-06
14026821246290.812026-07-07
146977912953104.282026-08-08
153687613444117.752026-09-09
160397313935-452026-01-10
1670107014421144.692026-02-11

Expected output

First 5 of 35 rows
transaction_datenet_revenue
2026-01-10-45
2026-04-011141.47
2026-04-021932.72
2026-04-03414.09
2026-04-0450.4

Worked solution and explanation

Two date scopes hiding in one revenue report

This is two row sets with different date scopes dressed up as a daily ledger. Purchases and refunds share total_amount, told apart only by sign, and the April-to-June window clamps the purchases but not the refunds. Anyone can total a day's rows; what separates candidates is whether the January refund still counts. Put the window on every row and the '-45.0' day on '2026-01-10' disappears, so the quarter reads richer than the money that actually moved.

Building it

Step 1: Select the quarter's purchases

Keep rows where total_amount > 0 and transaction_date sits between '2026-04-01' and '2026-06-30'. The strict > 0 is doing work: the voided sale on '2026-06-06' carries '0.0' and is neither a purchase nor a refund.

Step 2: Select every refund

Keep rows where total_amount < 0 with no date condition at all. This is the branch candidates get wrong, and on this data it is the only way the January refund reaches the result.

Step 3: Stack the sets, then total by day

UNION ALL keeps every row from both sides; a plain UNION would collapse two identical purchases on the same day into one. Then GROUP BY transaction_date with SUM(total_amount): refunds are already negative, so the sum is the net. Return the days earliest first, the way the ledger reads.

Asymmetric date filter for purchases vs refunds
WITH ledger_rows AS (
    SELECT transaction_date, total_amount
    FROM transactions
    WHERE total_amount > 0
      AND transaction_date >= '2026-04-01'
      AND transaction_date <= '2026-06-30'
    UNION ALL
    SELECT transaction_date, total_amount
    FROM transactions
    WHERE total_amount < 0
)
SELECT transaction_date, SUM(total_amount) AS net_revenue
FROM ledger_rows
GROUP BY transaction_date
ORDER BY transaction_date
The flat filter that looks right
SELECT transaction_date, SUM(total_amount) AS net_revenue
FROM transactions
WHERE transaction_date BETWEEN '2026-04-01' AND '2026-06-30'
GROUP BY transaction_date
ORDER BY transaction_date

Wrong twice on this data: it drops the January refund and keeps the June 6 voided sale.

Window on every row
A WHERE transaction_date BETWEEN ... over the whole table loses the '-45.0' refund day and keeps the '0.0' voided sale, so the result gains a June 6 day that should not exist.
Window on purchases only
The window sits in the purchase branch beside total_amount > 0; the refund branch carries only total_amount < 0, so refunds count whatever their date and voided sales never enter.

Common follow-up questions