cswork / libraryOpen practice workspace

Coding questions · TikTok

Compute 7-day rolling complaint/order ratio in SQL

This question evaluates SQL data manipulation skills, specifically time-based aggregations, sliding window functions, joins to include entities with no events, and numeric casting within the Data Manipulation (SQL/Python) domain.

SQL only. Given the schema and sample data below, write a single Postgres query (no procedural code) to compute a 7-day rolling complaint-to-order ratio per seller per day. Use "today" = 2025-09-01, so the 7-day window for each date d is [d-6, d], inclusive. Count events by date using event_time::date. If the denominator (orders) in the window is 0, return NULL for the ratio. Also return an overall ratio (aggregated across sellers) for 2025-09-01. Output columns for part (a): dt, seller_id, orders_7d, complaints_7d, ratio_7d (decimal with 4 decimals). Output columns for part (b): dt='2025-09-01', seller_id='ALL', orders_7d, complaints_7d, ratio_7d.

Schema:

  • sellers(seller_id INT PRIMARY KEY, country TEXT)
  • events(event_time TIMESTAMP, seller_id INT REFERENCES sellers, event_type TEXT CHECK (event_type IN ('order','complaint')))

Sample tables (minimal):

sellers
+----------+---------+
| seller_id| country |
+----------+---------+
| 101 | US |
| 102 | CN |
| 103 | US |
+----------+---------+
events
+---------------------+-----------+------------+
| event_time | seller_id | event_type |
+---------------------+-----------+------------+
| 2025-08-26 10:00:00 | 101 | order |
| 2025-08-26 12:00:00 | 101 | complaint |
| 2025-08-27 09:00:00 | 102 | order |
| 2025-08-27 11:30:00 | 101 | order |
| 2025-08-28 14:00:00 | 103 | order |
| 2025-08-29 15:00:00 | 101 | complaint |
| 2025-08-30 08:00:00 | 102 | order |
| 2025-08-30 18:20:00 | 102 | complaint |
| 2025-08-31 10:00:00 | 101 | order |
| 2025-08-31 20:00:00 | 103 | complaint |
| 2025-09-01 07:00:00 | 101 | complaint |
| 2025-09-01 09:15:00 | 103 | order |
+---------------------+-----------+------------+

Requirements/hints:

  • Generate all dates from 2025-08-26 to 2025-09-01 and join so that sellers with no events still emit rows.
  • Use window functions over date ranges (not ROWS BETWEEN) so days with no events are still included.
  • Avoid double-counting events; ensure event_type filters are correct.
  • Be careful with integer division; cast to numeric for the ratio.

7-day rolling complaint-to-order ratio per seller per day

SQL only (PostgreSQL). Using the schema and sample data below, write a single PostgreSQL query (no procedural code) that computes a 7-day rolling complaint-to-order ratio per seller per day.

Task

Consider the date range 2025-05-26 to 2025-06-01 (inclusive). For each date dt in this range, the 7-day window is every event whose event_time::date falls between dt - 6 days and dt, inclusive. Bucket events to a day using event_time::date.

For each (dt, seller_id) pair, compute:

  • orders_7d — the number of 'order' events in that 7-day window
  • complaints_7d — the number of 'complaint' events in that 7-day window
  • ratio_7d — complaints_7d / orders_7d, rounded to 4 decimal places. If orders_7d = 0, return NULL for ratio_7d (do not divide by zero).

Every seller must appear for every date in the range, even if it has no events in the window — generate the full date series from 2025-05-26 to 2025-06-01 and cross-join it with all sellers, so sellers with no events still emit rows (with zero counts and a NULL ratio).

Required output

Return exactly these columns, one row per (dt, seller_id):

columnmeaning
dtthe calendar date in the range
seller_idthe seller
orders_7dcount of 'order' events in the trailing 7-day window ending at dt
complaints_7dcount of 'complaint' events in the trailing 7-day window ending at dt
ratio_7dcomplaints_7d / orders_7d rounded to 4 decimals, or NULL when orders_7d = 0

Sort the result by dt ascending, then seller_id ascending.

Schema

  • sellers(seller_id INT PRIMARY KEY, country TEXT)
  • events(event_time TIMESTAMP, seller_id INT REFERENCES sellers, event_type TEXT CHECK (event_type IN ('order','complaint')))

Table sellers

columntypeconstraints
seller_idINTPRIMARY KEY, NOT NULL
countryTEXTNOT NULL

Sample data:

countryseller_id
US101
CN102
US103

Table events

columntypeconstraints
event_timeTIMESTAMPNOT NULL
seller_idINTNOT NULL, REFERENCES sellers(seller_id)
event_typeTEXTNOT NULL, CHECK (event_type IN ('order','complaint'))

Sample data:

event_timeevent_typeseller_id
2025-05-26 10:00:00order101
2025-05-26 12:00:00complaint101
2025-05-27 09:00:00order102
2025-05-27 11:30:00order101
2025-05-28 14:00:00order103
2025-05-29 15:00:00complaint101
2025-05-30 08:00:00order102
2025-05-30 18:20:00complaint102
2025-05-31 10:00:00order101
2025-05-31 20:00:00complaint103
2025-06-01 07:00:00complaint101
2025-06-01 09:15:00order103

Expected output

One row per (date, seller) for dt from 2025-05-26 to 2025-06-01 inclusive (21 rows for 7 dates x 3 sellers), with trailing 7-day order/complaint counts and their ratio (NULL when orders_7d = 0), ordered by dt then seller_id.

complaints_7ddtorders_7dratio_7dseller_id
12025-05-26T00:00:00+00:0011.0000101
02025-05-26T00:00:00+00:000NULL102
02025-05-26T00:00:00+00:000NULL103
12025-05-27T00:00:00+00:0020.5000101
02025-05-27T00:00:00+00:0010.0000102
02025-05-27T00:00:00+00:000NULL103
12025-05-28T00:00:00+00:0020.5000101
02025-05-28T00:00:00+00:0010.0000102
02025-05-28T00:00:00+00:0010.0000103
22025-05-29T00:00:00+00:0021.0000101
02025-05-29T00:00:00+00:0010.0000102
02025-05-29T00:00:00+00:0010.0000103
22025-05-30T00:00:00+00:0021.0000101
12025-05-30T00:00:00+00:0020.5000102
02025-05-30T00:00:00+00:0010.0000103
22025-05-31T00:00:00+00:0030.6667101
12025-05-31T00:00:00+00:0020.5000102
12025-05-31T00:00:00+00:0011.0000103
32025-06-01T00:00:00+00:0031.0000101
12025-06-01T00:00:00+00:0020.5000102
12025-06-01T00:00:00+00:0020.5000103

Hints

  1. Build a dense grid first: generate_series over the date range CROSS JOIN sellers, then LEFT JOIN your per-day counts and COALESCE missing counts to 0 — this is what lets every seller appear on every day and makes the rolling window gap-safe.
  2. For the trailing 7-day total, use a window frame RANGE BETWEEN INTERVAL '6 days' PRECEDING AND CURRENT ROW ordered by the date column (a calendar-aware RANGE, not a ROWS frame).
  3. Cast the counts to ::numeric before dividing so you get decimals instead of integer division, and wrap the division in a CASE WHEN orders_7d = 0 THEN NULL to avoid divide-by-zero.

Overall 7-day rolling complaint/order ratio across all sellers for a specific date

Using the same schema and date logic as in Question 1, write a PostgreSQL query that returns a single row with the overall 7-day rolling complaint-to-order ratio aggregated across all sellers for dt = '2025-06-01'.

The 7-day window for dt = '2025-06-01' is all events where event_time::date is between '2025-05-26' and '2025-06-01', inclusive.

Output columns:

  • dt (should be '2025-06-01')
  • seller_id (use the literal string 'ALL')
  • orders_7d (total orders across all sellers in the window)
  • complaints_7d (total complaints across all sellers in the window)
  • ratio_7d = complaints_7d / orders_7d as a decimal with 4 decimal places (NULL if orders_7d = 0).

Table sellers

columntypeconstraints
seller_idINTPRIMARY KEY, NOT NULL
countryTEXTNOT NULL

Sample data:

countryseller_id
US101
CN102
US103

Table events

columntypeconstraints
event_timeTIMESTAMPNOT NULL
seller_idINTNOT NULL, REFERENCES sellers(seller_id)
event_typeTEXTNOT NULL, CHECK (event_type IN ('order','complaint'))

Sample data:

event_timeevent_typeseller_id
2025-05-26 10:00:00order101
2025-05-26 12:00:00complaint101
2025-05-27 09:00:00order102
2025-05-27 11:30:00order101
2025-05-28 14:00:00order103
2025-05-29 15:00:00complaint101
2025-05-30 08:00:00order102
2025-05-30 18:20:00complaint102
2025-05-31 10:00:00order101
2025-05-31 20:00:00complaint103
2025-06-01 07:00:00complaint101
2025-06-01 09:15:00order103

Expected output

A single row giving the total 7-day rolling orders and complaints across all sellers for dt = '2025-06-01', and their ratio.

complaints_7ddtorders_7dratio_7dseller_id
52025-06-0170.7143ALL

Hints

  1. You can reuse the rolling 7-day per-seller metrics from Question 1 and then aggregate them for dt = '2025-06-01'.
  2. Alternatively, directly sum orders and complaints across all sellers over the date range '2025-05-26' to '2025-06-01' and compute the ratio with numeric division.