cswork / libraryOpen practice workspace

Coding questions · Coinbase

Compute adoption, latency, and cross-region transactions

This question evaluates proficiency in SQL-based data manipulation and analytics, including calculating cohort adoption and transaction rates, performing date arithmetic for time-to-first-transaction, identifying cross-region transactions, and computing distributional percentiles.

Invented schema:

users(user_id INT PRIMARY KEY, user_region STRING, adopted_at TIMESTAMP NULL)
transactions(txn_id INT PRIMARY KEY, user_id INT, txn_ts TIMESTAMP, txn_region STRING)
Sample rows:
users
user_id | user_region | adopted_at
1 | US | 2025-07-15 10:00:00
2 | US | null
3 | EU | 2025-08-02 09:12:00
4 | APAC | 2025-08-20 18:30:00
5 | EU | 2025-08-25 12:00:00
6 | US | 2025-08-30 23:59:00
transactions
txn_id | user_id | txn_ts | txn_region
101 | 1 | 2025-08-01 08:00:00 | US
102 | 1 | 2025-08-16 10:00:00 | CA
103 | 3 | 2025-08-10 12:10:00 | EU
104 | 4 | 2025-08-25 07:45:00 | APAC
105 | 4 | 2025-08-26 07:45:00 | US
106 | 5 | 2025-08-30 07:45:00 | EU
107 | 6 | 2025-09-01 00:10:00 | US

Write ANSI SQL for the following: A) Adoption_rate and transaction_rate per user_region for the month 2025-08-01 to 2025-08-31 (inclusive). Definitions: adoption_rate = users with adopted_at in August 2025 divided by total users in that region; transaction_rate = distinct users in that region having ≥1 transaction with txn_ts in August 2025 divided by total users in that region. Return region, adoption_rate, transaction_rate. B) For each user, compute days_to_first_txn = DATE_DIFF(first transaction timestamp, adopted_at) in days. Only include users with non-null adopted_at and first transaction timestamp ≥ adopted_at. Return user_id and days_to_first_txn, and also a separate query that returns the p10, p50, p90 of days_to_first_txn. C) Define a cross-region sale as any transaction where txn_region ≠ the user’s first_txn_region (the region of the earliest transaction in the user’s lifetime, not limited to August). Return all transactions with txn_ts in 2025-08-01..2025-08-31 flagged with is_cross_region plus columns: txn_id, user_id, txn_ts, txn_region, first_txn_region.

Adoption rate and transaction rate per region (August 2025)

You are given two tables: users and transactions.

For the month 2025-08-01 to 2025-08-31 (inclusive), compute the following per users.user_region:

  • adoption_rate = (# users whose adopted_at is in August 2025) / (total # users in that region)
  • transaction_rate = (# distinct users in that region with >= 1 transaction whose txn_ts is in August 2025) / (total # users in that region)

Return: region, adoption_rate, transaction_rate. Use decimal rates (not percentages).

Table users

columntypeconstraints
user_idINTPRIMARY KEY, NOT NULL
user_regionVARCHAR(10)NOT NULL
adopted_atTIMESTAMP

Sample data:

adopted_atuser_iduser_region
2025-07-15 10:00:001US
NULL2US
2025-08-02 09:12:003EU
2025-08-20 18:30:004APAC
2025-08-25 12:00:005EU
2025-08-30 23:59:006US

Table transactions

columntypeconstraints
txn_idINTPRIMARY KEY, NOT NULL
user_idINTNOT NULL
txn_tsTIMESTAMPNOT NULL
txn_regionVARCHAR(10)NOT NULL

Sample data:

txn_idtxn_regiontxn_tsuser_id
101US2025-08-01 08:00:001
102CA2025-08-16 10:00:001
103EU2025-08-10 12:10:003
104APAC2025-08-25 07:45:004
105US2025-08-26 07:45:004
106EU2025-08-30 07:45:005
107US2025-09-01 00:10:006

Expected output

One row per user_region with adoption_rate and transaction_rate for 2025-08-01..2025-08-31 (inclusive).

adoption_rateregiontransaction_rate
1APAC1
1EU1
0.3333US0.3333

Hints

  1. Compute denominators (total users per region) once, then LEFT JOIN numerator aggregates.
  2. Use an inclusive August filter by using [>= 2025-08-01) and (< 2025-09-01).

Days from adoption to first transaction + p10/p50/p90

Using users and transactions, compute days from adoption to each user's first transaction and the p10, p50, and p90 percentiles of those day counts. Return one combined PostgreSQL result set with columns result_set, user_id, days_to_first_txn, p10, p50, and p90. Use result_set = 'user_days' for per-user rows and result_set = 'percentiles' for the aggregate percentile row; columns that do not apply should be NULL. Only include users whose first transaction timestamp is on or after adopted_at.

Table users

columntypeconstraints
user_idINTPRIMARY KEY, NOT NULL
user_regionVARCHAR(10)NOT NULL
adopted_atTIMESTAMP

Sample data:

adopted_atuser_iduser_region
2025-07-15 10:00:001US
NULL2US
2025-08-02 09:12:003EU
2025-08-20 18:30:004APAC
2025-08-25 12:00:005EU
2025-08-30 23:59:006US

Table transactions

columntypeconstraints
txn_idINTPRIMARY KEY, NOT NULL
user_idINTNOT NULL
txn_tsTIMESTAMPNOT NULL
txn_regionVARCHAR(10)NOT NULL

Sample data:

txn_idtxn_regiontxn_tsuser_id
101US2025-08-01 08:00:001
102CA2025-08-16 10:00:001
103EU2025-08-10 12:10:003
104APAC2025-08-25 07:45:004
105US2025-08-26 07:45:004
106EU2025-08-30 07:45:005
107US2025-09-01 00:10:006

Expected output

Per-user days to first transaction plus one percentile summary row.

days_to_first_txnp10p50p90result_setuser_id
16NULLNULLNULLuser_days1
8NULLNULLNULLuser_days3
4NULLNULLNULLuser_days4
4NULLNULLNULLuser_days5
1NULLNULLNULLuser_days6
NULL2.2412.8percentilesNULL

Hints

  1. Compute first transaction per user in a CTE.
  2. Use percentile_cont over the per-user day differences.
  3. Use NULLs for columns that do not apply to the row type.

Flag cross-region transactions in August 2025

You are given two tables: users and transactions.

Define a user's first_txn_region as the txn_region of their earliest transaction in their lifetime (earliest txn_ts), not limited to August.

Define a cross-region sale as any transaction where txn_region != first_txn_region for that user.

Return all transactions with txn_ts in 2025-08-01 to 2025-08-31 (inclusive), with columns:

  • txn_id, user_id, txn_ts formatted as YYYY-MM-DD HH24:MI:SS, txn_region, first_txn_region, is_cross_region (1 if cross-region else 0).

Table users

columntypeconstraints
user_idINTPRIMARY KEY, NOT NULL
user_regionVARCHAR(10)NOT NULL
adopted_atTIMESTAMP

Sample data:

adopted_atuser_iduser_region
2025-07-15 10:00:001US
NULL2US
2025-08-02 09:12:003EU
2025-08-20 18:30:004APAC
2025-08-25 12:00:005EU
2025-08-30 23:59:006US

Table transactions

columntypeconstraints
txn_idINTPRIMARY KEY, NOT NULL
user_idINTNOT NULL
txn_tsTIMESTAMPNOT NULL
txn_regionVARCHAR(10)NOT NULL

Sample data:

txn_idtxn_regiontxn_tsuser_id
101US2025-08-01 08:00:001
102CA2025-08-16 10:00:001
103EU2025-08-10 12:10:003
104APAC2025-08-25 07:45:004
105US2025-08-26 07:45:004
106EU2025-08-30 07:45:005
107US2025-09-01 00:10:006

Expected output

All August 2025 transactions with the user's first_txn_region and a 1/0 flag indicating whether the transaction region differs from that first region.

first_txn_regionis_cross_regiontxn_idtxn_regiontxn_tsuser_id
US0101US2025-08-01 08:00:001
US1102CA2025-08-16 10:00:001
EU0103EU2025-08-10 12:10:003
APAC0104APAC2025-08-25 07:45:004
APAC1105US2025-08-26 07:45:004
EU0106EU2025-08-30 07:45:005

Hints

  1. Compute the first transaction region over all transactions before applying the August filter.
  2. Use ROW_NUMBER ordered by txn_ts, txn_id to make the first-region tie-break deterministic.
  3. Use TO_CHAR to format txn_ts for PostgreSQL output.