cswork / libraryOpen practice workspace

Coding questions · Roblox

Write SQL to flag suspect payments and chargebacks

This question evaluates proficiency with SQL conditional logic (CASE WHEN), JOINs, date/time arithmetic, aggregation, and encoding business rules for flagging suspect payments and chargebacks, and is categorized under Data Manipulation (SQL/Python) for a Data Scientist role.

Assume today is 2025-09-01. Using only CASE WHEN and JOINs (no window functions required), write a single SQL query that produces, for each user with any payment in the last 7 days [2025-08-25 00:00:00, 2025-09-01 00:00:00), the columns: user_id, payments_last_7d, suspect_cnt_last_7d, any_chargeback_last_60d (Y/N). A payment is “suspect” if at least one holds: (a) user is a minor on the payment timestamp (<18 years), (b) users.country != payments.ip_country, or (c) payment occurred within 24 hours of the user’s account creation AND amount_usd > 10. any_chargeback_last_60d is Y if the user has any chargeback linked to any of their payments with payment_ts in [2025-07-03 00:00:00, 2025-09-01 00:00:00) and cb_ts ≤ 2025-09-01.

Schema and tiny sample data: users

  • user_id (int), birthdate (date), country (text), created_at (timestamp)
user_idbirthdatecountrycreated_at
12012-05-10US2023-11-01 09:00:00
22000-03-03US2022-02-14 10:00:00
32010-12-31CA2025-08-20 08:00:00

payments

  • payment_id (int), user_id (int), amount_usd (numeric), currency (text), payment_ts (timestamp), ip_country (text), device_id (text)
payment_iduser_idamount_usdcurrencypayment_tsip_countrydevice_id
1014.99USD2025-08-28 12:00:00USA
11112.99USD2025-08-28 12:30:00USA
12219.99USD2025-08-31 20:00:00MXB
1330.99CAD2025-08-25 09:00:00CAC

chargebacks

  • cb_id (int), payment_id (int), cb_ts (timestamp), reason_code (text)
cb_idpayment_idcb_tsreason_code
100122025-09-15 10:00:00FRAUD_CARDHOLDER_DISPUTE

Edge conditions to handle: precise date bounds, age computed on payment_ts (birthdate+18 years), left joins so users without chargebacks still appear, and ensure only users with ≥1 payment in the last 7 days are returned.

Flag suspect payments and chargebacks over recent windows

Assume the current date is 2025-06-01. Using only CASE WHEN expressions and JOINs (no window functions required), write a single SQL query that returns, for each user who has at least one payment in the last 7 days (i.e., payment_ts in the range [2025-05-25 00:00:00, 2025-06-01 00:00:00)), the following columns:

  • user_id
  • payments_last_7d: count of that user's payments with payment_ts in [2025-05-25 00:00:00, 2025-06-01 00:00:00)
  • suspect_cnt_last_7d: count of that user's payments in that same 7-day window that are "suspect"
  • any_chargeback_last_60d: 'Y' if the user has at least one chargeback linked to any of their payments where the payment_ts is in [2025-04-02 00:00:00, 2025-06-01 00:00:00) and the chargeback timestamp cb_ts <= 2025-06-01; otherwise 'N'.

A payment is considered "suspect" if at least one of the following holds at the time of that payment: (a) The user is a minor at the payment timestamp (payment_ts < birthdate + 18 years). (b) users.country <> payments.ip_country. (c) The payment occurred within 24 hours of the user's account creation (payment_ts < created_at + 24 hours) AND amount_usd > 10.

Edge conditions to handle:

  • Use the exact timestamp bounds as specified (inclusive lower bound, exclusive upper bound for the time ranges).
  • Compute age at the payment timestamp via birthdate + interval '18 years'.
  • Use LEFT JOINs so that users without matching chargebacks can still appear.
  • Ensure that only users with at least one payment in the last 7 days are returned.

Write a single SQL query that produces the requested columns using the schema and sample data below.

Table users

columntypeconstraints
user_idINTPRIMARY KEY, NOT NULL
birthdateDATENOT NULL
countryVARCHAR(2)NOT NULL
created_atTIMESTAMPNOT NULL

Sample data:

birthdatecountrycreated_atuser_id
2006-05-10US2025-05-27 13:00:001
2000-03-03US2022-02-14 10:00:002
2010-12-31CA2025-05-20 08:00:003

Table payments

columntypeconstraints
payment_idINTPRIMARY KEY, NOT NULL
user_idINTNOT NULL
amount_usdNUMERIC(10,2)NOT NULL
currencyVARCHAR(3)NOT NULL
payment_tsTIMESTAMPNOT NULL
ip_countryVARCHAR(2)NOT NULL
device_idVARCHAR(50)NOT NULL

Sample data:

amount_usdcurrencydevice_idip_countrypayment_idpayment_tsuser_id
4.99USDAUS102025-05-28 12:00:001
12.99USDAUS112025-05-28 12:30:001
19.99USDBMX122025-05-29 20:00:002
0.99CADCCA132025-05-25 09:00:003

Table chargebacks

columntypeconstraints
cb_idINTPRIMARY KEY, NOT NULL
payment_idINTNOT NULL
cb_tsTIMESTAMPNOT NULL
reason_codeVARCHAR(100)NOT NULL

Sample data:

cb_idcb_tspayment_idreason_code
1002025-05-31 10:00:0012FRAUD_CARDHOLDER_DISPUTE

Expected output

One row per user who has at least one payment with payment_ts in [2025-05-25 00:00:00, 2025-06-01 00:00:00). payments_last_7d counts all such payments; suspect_cnt_last_7d counts those payments that meet any of the suspect criteria; any_chargeback_last_60d is 'Y' if the user has any chargeback tied to a payment with payment_ts in [2025-04-02 00:00:00, 2025-06-01 00:00:00) and cb_ts <= 2025-06-01, otherwise 'N'. On the sample data, all three users have payments in the last 7 days, and only user 2 has a qualifying chargeback.

any_chargeback_last_60dpayments_last_7dsuspect_cnt_last_7duser_id
N211
Y112
N113

Hints

  1. Use conditional aggregation with SUM(CASE WHEN ...) to count payments in the different time windows and to flag suspect payments.
  2. Compute whether a user is a minor at payment time by comparing payment_ts with birthdate + INTERVAL '18 years', and use a HAVING clause to keep only users with at least one payment in the last 7 days.