cswork / libraryOpen practice workspace

Coding questions · DoorDash

Write complex SQL on DoorDash data

This question evaluates proficiency in complex SQL querying and data manipulation, including joins across tables, time-based filtering, window functions, null and edge-case handling, aggregations, and integrating promotion and order-event data.

You are given the following BigQuery-style schema and tiny samples (assume timestamps are UTC; assume promotions.discount_amount is the applied discount). Use today = 2025-09-01 when interpreting “last 7 days.” Write precise SQL answers for (a)–(f), explaining any windowing or edge-case assumptions.

customers(customer_id INT, signup_date DATE, city STRING)
Sample:
customer_id | signup_date | city
1 | 2025-07-10 | SF
2 | 2025-08-01 | SF
3 | 2025-08-20 | NYC
merchants(merchant_id INT, name STRING, city STRING, cuisine STRING)
Sample:
merchant_id | name | city | cuisine
10 | SushiCo | SF | Japanese
11 | TacoTown | SF | Mexican
12 | PizzaBox | NYC | Italian
dashers(dasher_id INT, signup_date DATE, vehicle_type STRING)
Sample:
dasher_id | signup_date | vehicle_type
100 | 2025-06-01 | car
101 | 2025-08-15 | bike
promotions(promo_id INT, promo_type STRING, discount_amount NUMERIC, start_date DATE, end_date DATE)
Sample:
promo_id | promo_type | discount_amount | start_date | end_date
900 | pct10 | 10.00 | 2025-08-01 | 2025-08-31
901 | flat5 | 5.00 | 2025-08-10 | 2025-09-10
orders(order_id INT, customer_id INT, merchant_id INT, dasher_id INT, created_at TIMESTAMP, accepted_at TIMESTAMP, picked_up_at TIMESTAMP, delivered_at TIMESTAMP, canceled_at TIMESTAMP, subtotal NUMERIC, delivery_fee NUMERIC, tip_amount NUMERIC, promo_id INT, status STRING)
Sample:
order_id | customer_id | merchant_id | dasher_id | created_at | accepted_at | picked_up_at | delivered_at | canceled_at | subtotal | delivery_fee | tip_amount | promo_id | status
5001 | 1 | 10 | 100 | 2025-08-28 12:00 | 2025-08-28 12:02 | 2025-08-28 12:20 | 2025-08-28 12:35 | null | 30.00 | 3.99 | 5.00 | 900 | delivered
5002 | 2 | 11 | null| 2025-08-29 18:05 | null | null | null | 2025-08-29 18:10 | 15.00 | 2.99 | 0.00 | null | canceled
5003 | 1 | 11 | 101 | 2025-08-31 19:00 | 2025-08-31 19:04 | 2025-08-31 19:20 | null | null | 20.00 | 2.99 | 3.00 | 901 | in_progress
5004 | 3 | 12 | 100 | 2025-08-24 11:30 | 2025-08-24 11:31 | null | 2025-08-24 12:10 | null | 22.00 | 4.99 | 0.00 | null | delivered
order_events(order_id INT, event_time TIMESTAMP, event_type STRING)
Sample:
order_id | event_time | event_type
5001 | 2025-08-28 12:00 | created
5001 | 2025-08-28 12:02 | accepted
5001 | 2025-08-28 12:20 | picked_up
5001 | 2025-08-28 12:35 | delivered
5002 | 2025-08-29 18:05 | created
5002 | 2025-08-29 18:10 | canceled
5003 | 2025-08-31 19:00 | created
5003 | 2025-08-31 19:04 | accepted
5004 | 2025-08-24 11:30 | created
5004 | 2025-08-24 11:31 | accepted
5004 | 2025-08-24 12:10 | delivered

Tasks: (a) Compute daily on-time delivery rate per city for the last 7 days (2025-08-26 to 2025-09-01). Define on-time as delivered_at <= created_at + INTERVAL 45 MINUTE for non-canceled orders; treat null delivered_at as late. Show city, date, on_time_deliveries, total_eligible, on_time_rate. (b) For August 2025, list top 3 merchants per city by GMV where GMV = subtotal + delivery_fee + tip_amount − COALESCE(promotions.discount_amount,0), excluding canceled orders. Break ties by fewer cancellations, then by merchant_id. Output city, merchant_id, rank_in_city, gmv, cancellations. (c) Using order_events, find orders that have delivered event without a preceding picked_up event (strictly earlier). Return order_id and a minimal event timeline proving the anomaly. (d) Compute weekly dasher acceptance rate: among orders created in a given ISO week and assigned to a dasher, the share with accepted_at within 3 minutes of created_at. Exclude orders canceled before accepted_at. Output dasher_id, week_start_date, assigned_orders, accepted_within_3m, acceptance_rate. (e) Find customers who placed exactly two lifetime orders and both were canceled. Return customer_id and the two canceled_at timestamps in ascending order. (f) For each merchant in August 2025, estimate uplift in average order value (AOV) when a promo is applied vs not applied. Output merchant_id, promo_orders, non_promo_orders, aov_promo, aov_nonpromo, diff. Then describe (briefly) how you would compute a 95% CI for diff via a SQL-based bootstrap or a Python snippet using the same tables.

Top merchants per city by GMV with tie-breakers

For May 2025 (2025-05-01 to 2025-05-31 inclusive), list the top 3 merchants per city by GMV.

Definitions:

  • Exclude canceled orders from GMV.
  • GMV per order = subtotal + delivery_fee + tip_amount - COALESCE(promotions.discount_amount, 0).
  • cancellations = number of canceled orders (status = 'canceled') for that merchant during May 2025.

Ranking rules (per city):

  1. Higher GMV first
  2. If tied on GMV, fewer cancellations first
  3. If still tied, lower merchant_id first

Output columns:

  • city
  • merchant_id
  • rank_in_city (1..3)
  • gmv
  • cancellations

Table customers

columntypeconstraints
customer_idINTPRIMARY KEY, NOT NULL
signup_dateDATENOT NULL
cityVARCHAR(50)NOT NULL

Sample data:

citycustomer_idsignup_date
SF12025-04-10
SF22025-05-01
NYC32025-05-20
NYC42025-03-15
SF52025-05-01

Table merchants

columntypeconstraints
merchant_idINTPRIMARY KEY, NOT NULL
nameVARCHAR(100)NOT NULL
cityVARCHAR(50)NOT NULL
cuisineVARCHAR(50)NOT NULL

Sample data:

citycuisinemerchant_idname
SFJapanese10SushiCo
SFMexican11TacoTown
SFAmerican14BurgerBay
NYCItalian12PizzaBox
NYCAmerican13BagelHub
NYCJapanese15RamenNY

Table dashers

columntypeconstraints
dasher_idINTPRIMARY KEY, NOT NULL
signup_dateDATENOT NULL
vehicle_typeVARCHAR(20)NOT NULL

Sample data:

dasher_idsignup_datevehicle_type
1002025-01-01car
1012025-05-15bike
1022025-05-20car

Table promotions

columntypeconstraints
promo_idINTPRIMARY KEY, NOT NULL
promo_typeVARCHAR(50)NOT NULL
discount_amountDECIMAL(10,2)NOT NULL
start_dateDATENOT NULL
end_dateDATENOT NULL

Sample data:

discount_amountend_datepromo_idpromo_typestart_date
102025-05-31900pct102025-05-01
52025-06-10901flat52025-05-15

Table orders

columntypeconstraints
order_idINTPRIMARY KEY, NOT NULL
customer_idINTNOT NULL
merchant_idINTNOT NULL
dasher_idINT
created_atTIMESTAMPNOT NULL
accepted_atTIMESTAMP
picked_up_atTIMESTAMP
delivered_atTIMESTAMP
canceled_atTIMESTAMP
subtotalDECIMAL(10,2)NOT NULL
delivery_feeDECIMAL(10,2)NOT NULL
tip_amountDECIMAL(10,2)NOT NULL
promo_idINT
statusVARCHAR(20)NOT NULL

Sample data:

accepted_atcanceled_atcreated_atcustomer_iddasher_iddelivered_atdelivery_feemerchant_idorder_idpicked_up_atpromo_idstatussubtotaltip_amount
2025-05-05 12:02:00NULL2025-05-05 12:00:0021002025-05-05 12:35:003.991460072025-05-05 12:15:00NULLdelivered254
2025-05-10 13:05:00NULL2025-05-10 13:00:0011002025-05-10 13:30:003.991060082025-05-10 13:20:00900delivered406
2025-05-12 11:01:00NULL2025-05-12 11:00:0031002025-05-12 11:30:004.991260102025-05-12 11:10:00NULLdelivered182
NULL2025-05-15 18:05:002025-05-15 18:00:001100NULL2.99106009NULLNULLcanceled200
2025-05-18 09:02:00NULL2025-05-18 09:00:0041012025-05-18 09:40:002.991360112025-05-18 09:10:00901delivered121
2025-05-22 19:02:00NULL2025-05-22 19:00:0031022025-05-22 19:35:003.991560122025-05-22 19:15:00NULLdelivered305
NULL2025-05-23 20:05:002025-05-23 20:00:003102NULL3.99156016NULLNULLcanceled160
2025-05-26 12:02:00NULL2025-05-26 12:00:0011002025-05-26 12:40:003.991060012025-05-26 12:20:00900delivered305
2025-05-27 18:08:00NULL2025-05-27 18:05:002NULL2025-05-27 19:10:002.991160022025-05-27 18:30:00NULLdelivered150
NULL2025-05-28 10:05:002025-05-28 10:00:005101NULL2.99106014NULLNULLcanceled120
2025-05-29 19:05:00NULL2025-05-29 19:00:001101NULL2.991160032025-05-29 19:25:00901in_progress203
NULL2025-05-29 10:07:002025-05-29 10:00:005101NULL3.99146015NULLNULLcanceled140
2025-05-30 11:31:00NULL2025-05-30 11:30:0031002025-05-30 12:05:004.99126004NULLNULLdelivered220
2025-05-31 20:01:00NULL2025-05-31 20:00:0031022025-05-31 20:50:004.991260052025-05-31 20:10:00NULLdelivered282
NULL2025-05-25 20:10:002025-05-25 20:00:002NULLNULL2.99116013NULLNULLcanceled150
NULL2025-06-01 09:02:002025-06-01 09:00:004101NULL3.99136006NULLNULLcanceled180

Table order_events

columntypeconstraints
order_idINTNOT NULL
event_timeTIMESTAMPNOT NULL
event_typeVARCHAR(30)NOT NULL

Sample data:

event_timeevent_typeorder_id
2025-05-26 12:00:00created6001

Expected output

Top 3 merchants per city (in May 2025) by GMV, with the specified tie-breakers. GMV excludes canceled orders; cancellations counts canceled orders in May.

cancellationscitygmvmerchant_idrank_in_city
0NYC86.97121
1NYC38.99152
0NYC10.99133
2SF68.98101
1SF38.98112
1SF32.99143

Hints

  1. Compute GMV at the order level first (including discount) and then aggregate to merchant.
  2. Use ROW_NUMBER with ORDER BY (gmv DESC, cancellations ASC, merchant_id ASC).

Detect delivered-without-picked-up anomalies from event logs

Using order_events, find orders that have a 'delivered' event but do NOT have a 'picked_up' event strictly earlier than the delivered event.

Return:

  • order_id
  • created_time (timestamp of created event)
  • picked_up_time (timestamp of picked_up event; NULL if missing)
  • delivered_time (timestamp of delivered event)

If there are multiple events of the same type, use the earliest timestamp for that event type.

Table customers

columntypeconstraints
customer_idINTPRIMARY KEY, NOT NULL
signup_dateDATENOT NULL
cityVARCHAR(50)NOT NULL

Sample data:

citycustomer_idsignup_date
SF12025-04-10

Table merchants

columntypeconstraints
merchant_idINTPRIMARY KEY, NOT NULL
nameVARCHAR(100)NOT NULL
cityVARCHAR(50)NOT NULL
cuisineVARCHAR(50)NOT NULL

Sample data:

citycuisinemerchant_idname
NYCItalian12PizzaBox

Table dashers

columntypeconstraints
dasher_idINTPRIMARY KEY, NOT NULL
signup_dateDATENOT NULL
vehicle_typeVARCHAR(20)NOT NULL

Sample data:

dasher_idsignup_datevehicle_type
1002025-01-01car

Table promotions

columntypeconstraints
promo_idINTPRIMARY KEY, NOT NULL
promo_typeVARCHAR(50)NOT NULL
discount_amountDECIMAL(10,2)NOT NULL
start_dateDATENOT NULL
end_dateDATENOT NULL

Sample data:

discount_amountend_datepromo_idpromo_typestart_date
102025-05-31900pct102025-05-01

Table orders

columntypeconstraints
order_idINTPRIMARY KEY, NOT NULL
customer_idINTNOT NULL
merchant_idINTNOT NULL
dasher_idINT
created_atTIMESTAMPNOT NULL
accepted_atTIMESTAMP
picked_up_atTIMESTAMP
delivered_atTIMESTAMP
canceled_atTIMESTAMP
subtotalDECIMAL(10,2)NOT NULL
delivery_feeDECIMAL(10,2)NOT NULL
tip_amountDECIMAL(10,2)NOT NULL
promo_idINT
statusVARCHAR(20)NOT NULL

Sample data:

accepted_atcanceled_atcreated_atcustomer_iddasher_iddelivered_atdelivery_feemerchant_idorder_idpicked_up_atpromo_idstatussubtotaltip_amount
2025-05-30 11:31:00NULL2025-05-30 11:30:0031002025-05-30 12:05:004.99126004NULLNULLdelivered220

Table order_events

columntypeconstraints
order_idINTNOT NULL
event_timeTIMESTAMPNOT NULL
event_typeVARCHAR(30)NOT NULL

Sample data:

event_timeevent_typeorder_id
2025-05-30 11:30:00created6004
2025-05-30 11:31:00accepted6004
2025-05-30 12:05:00delivered6004
2025-05-26 12:00:00created6001
2025-05-26 12:20:00picked_up6001
2025-05-26 12:40:00delivered6001

Expected output

Orders where a delivered event exists but there is no picked_up event strictly earlier than delivered_time (picked_up missing or picked_up at/after delivered_time).

created_timedelivered_timeorder_idpicked_up_time
2025-05-30 11:30:002025-05-30 12:05:006004NULL

Hints

  1. Aggregate event timestamps per order with conditional MIN.
  2. Filter delivered orders where pickup is missing or not earlier than delivery.

Customers with exactly two lifetime orders and both canceled

Find customers who placed exactly two lifetime orders and both orders were canceled.

Return:

  • customer_id
  • canceled_at_1 (earlier canceled_at, formatted as YYYY-MM-DD HH24:MI:SS)
  • canceled_at_2 (later canceled_at, formatted as YYYY-MM-DD HH24:MI:SS)

Assume canceled orders have status = 'canceled' and canceled_at is NOT NULL.

Table customers

columntypeconstraints
customer_idINTPRIMARY KEY, NOT NULL
signup_dateDATENOT NULL
cityVARCHAR(50)NOT NULL

Sample data:

citycustomer_idsignup_date
SF12025-04-10
SF52025-05-01

Table merchants

columntypeconstraints
merchant_idINTPRIMARY KEY, NOT NULL
nameVARCHAR(100)NOT NULL
cityVARCHAR(50)NOT NULL
cuisineVARCHAR(50)NOT NULL

Sample data:

citycuisinemerchant_idname
SFJapanese10SushiCo
SFAmerican14BurgerBay

Table dashers

columntypeconstraints
dasher_idINTPRIMARY KEY, NOT NULL
signup_dateDATENOT NULL
vehicle_typeVARCHAR(20)NOT NULL

Sample data:

dasher_idsignup_datevehicle_type
1012025-05-15bike

Table promotions

columntypeconstraints
promo_idINTPRIMARY KEY, NOT NULL
promo_typeVARCHAR(50)NOT NULL
discount_amountDECIMAL(10,2)NOT NULL
start_dateDATENOT NULL
end_dateDATENOT NULL

Sample data:

discount_amountend_datepromo_idpromo_typestart_date
52025-06-10901flat52025-05-15

Table orders

columntypeconstraints
order_idINTPRIMARY KEY, NOT NULL
customer_idINTNOT NULL
merchant_idINTNOT NULL
dasher_idINT
created_atTIMESTAMPNOT NULL
accepted_atTIMESTAMP
picked_up_atTIMESTAMP
delivered_atTIMESTAMP
canceled_atTIMESTAMP
subtotalDECIMAL(10,2)NOT NULL
delivery_feeDECIMAL(10,2)NOT NULL
tip_amountDECIMAL(10,2)NOT NULL
promo_idINT
statusVARCHAR(20)NOT NULL

Sample data:

accepted_atcanceled_atcreated_atcustomer_iddasher_iddelivered_atdelivery_feemerchant_idorder_idpicked_up_atpromo_idstatussubtotaltip_amount
NULL2025-05-28 10:05:002025-05-28 10:00:005101NULL2.99106014NULLNULLcanceled120
NULL2025-05-29 10:07:002025-05-29 10:00:005101NULL3.99146015NULLNULLcanceled140
2025-05-26 12:02:00NULL2025-05-26 12:00:0011002025-05-26 12:40:003.991060012025-05-26 12:20:00900delivered305

Table order_events

columntypeconstraints
order_idINTNOT NULL
event_timeTIMESTAMPNOT NULL
event_typeVARCHAR(30)NOT NULL

Sample data:

event_timeevent_typeorder_id
2025-05-28 10:00:00created6014

Expected output

Customers who have exactly two orders total and both have status='canceled', along with their cancellation timestamps ordered ascending.

canceled_at_1canceled_at_2customer_id
2025-05-28 10:05:002025-05-29 10:07:005

Hints

  1. Group by customer_id and keep only customers with COUNT(*) = 2 and two canceled orders.
  2. Use ARRAY_AGG(canceled_at ORDER BY canceled_at) to access the earlier and later cancellation timestamps.
  3. Use TO_CHAR rather than strftime in PostgreSQL.

Promo vs non-promo AOV uplift per merchant

For each merchant in May 2025 (2025-05-01 to 2025-05-31 inclusive), estimate uplift in average order value (AOV) when a promo is applied vs not applied.

Definitions:

  • Exclude canceled orders.
  • order_value = subtotal + delivery_fee + tip_amount - COALESCE(promotions.discount_amount, 0)
  • Promo order: promo_id IS NOT NULL

Output columns:

  • merchant_id
  • promo_orders
  • non_promo_orders
  • aov_promo
  • aov_nonpromo
  • diff = aov_promo - aov_nonpromo

If a merchant has zero promo orders or zero non-promo orders, the corresponding AOV (and diff) may be NULL.

Table customers

columntypeconstraints
customer_idINTPRIMARY KEY, NOT NULL
signup_dateDATENOT NULL
cityVARCHAR(50)NOT NULL

Sample data:

citycustomer_idsignup_date
SF12025-04-10
SF22025-05-01
NYC32025-05-20
NYC42025-03-15

Table merchants

columntypeconstraints
merchant_idINTPRIMARY KEY, NOT NULL
nameVARCHAR(100)NOT NULL
cityVARCHAR(50)NOT NULL
cuisineVARCHAR(50)NOT NULL

Sample data:

citycuisinemerchant_idname
SFJapanese10SushiCo
SFMexican11TacoTown
NYCItalian12PizzaBox
NYCAmerican13BagelHub
SFAmerican14BurgerBay
NYCJapanese15RamenNY

Table dashers

columntypeconstraints
dasher_idINTPRIMARY KEY, NOT NULL
signup_dateDATENOT NULL
vehicle_typeVARCHAR(20)NOT NULL

Sample data:

dasher_idsignup_datevehicle_type
1002025-01-01car
1012025-05-15bike
1022025-05-20car

Table promotions

columntypeconstraints
promo_idINTPRIMARY KEY, NOT NULL
promo_typeVARCHAR(50)NOT NULL
discount_amountDECIMAL(10,2)NOT NULL
start_dateDATENOT NULL
end_dateDATENOT NULL

Sample data:

discount_amountend_datepromo_idpromo_typestart_date
102025-05-31900pct102025-05-01
52025-06-10901flat52025-05-15

Table orders

columntypeconstraints
order_idINTPRIMARY KEY, NOT NULL
customer_idINTNOT NULL
merchant_idINTNOT NULL
dasher_idINT
created_atTIMESTAMPNOT NULL
accepted_atTIMESTAMP
picked_up_atTIMESTAMP
delivered_atTIMESTAMP
canceled_atTIMESTAMP
subtotalDECIMAL(10,2)NOT NULL
delivery_feeDECIMAL(10,2)NOT NULL
tip_amountDECIMAL(10,2)NOT NULL
promo_idINT
statusVARCHAR(20)NOT NULL

Sample data:

accepted_atcanceled_atcreated_atcustomer_iddasher_iddelivered_atdelivery_feemerchant_idorder_idpicked_up_atpromo_idstatussubtotaltip_amount
2025-05-05 12:02:00NULL2025-05-05 12:00:0021002025-05-05 12:35:003.991460072025-05-05 12:15:00NULLdelivered254
2025-05-10 13:05:00NULL2025-05-10 13:00:0011002025-05-10 13:30:003.991060082025-05-10 13:20:00900delivered406
2025-05-12 11:01:00NULL2025-05-12 11:00:0031002025-05-12 11:30:004.991260102025-05-12 11:10:00NULLdelivered182
2025-05-18 09:02:00NULL2025-05-18 09:00:0041012025-05-18 09:40:002.991360112025-05-18 09:10:00901delivered121
2025-05-22 19:02:00NULL2025-05-22 19:00:0031022025-05-22 19:35:003.991560122025-05-22 19:15:00NULLdelivered305
2025-05-26 12:02:00NULL2025-05-26 12:00:0011002025-05-26 12:40:003.991060012025-05-26 12:20:00900delivered305
2025-05-27 18:08:00NULL2025-05-27 18:05:002NULL2025-05-27 19:10:002.991160022025-05-27 18:30:00NULLdelivered150
2025-05-29 19:05:00NULL2025-05-29 19:00:001101NULL2.991160032025-05-29 19:25:00901in_progress203
2025-05-30 11:31:00NULL2025-05-30 11:30:0031002025-05-30 12:05:004.99126004NULLNULLdelivered220
2025-05-31 20:01:00NULL2025-05-31 20:00:0031022025-05-31 20:50:004.991260052025-05-31 20:10:00NULLdelivered282

Table order_events

columntypeconstraints
order_idINTNOT NULL
event_timeTIMESTAMPNOT NULL
event_typeVARCHAR(30)NOT NULL

Sample data:

event_timeevent_typeorder_id
2025-05-26 12:00:00created6001

Expected output

One row per merchant with counts and AOV split by whether promo_id is present, plus the difference (promo - non-promo).

aov_nonpromoaov_promodiffmerchant_idnon_promo_orderspromo_orders
NULL34.49NULL1002
17.9920.9931111
28.99NULLNULL1230
NULL10.99NULL1301
32.99NULLNULL1410
38.99NULLNULL1510

Hints

  1. Compute order_value once (including discount) and then average it conditionally by promo_id IS NULL/NOT NULL.
  2. NULL AOVs are expected if a merchant has no orders in one of the two groups.