Higher Performing Variant
An easy SQL interview practice problem on DataDriven. Write the query and run it against a real database, with instant feedback.
- Domain
- SQL
- Difficulty
- easy
- Seniority
- L3
- Asked in
- Airbnb
Problem
Several clients logged their variant labels in ab_results with their own capitalization. Before those labels are reconciled, an analyst wants to know which label, as logged, has the highest average metric value across every test. Return that one label with its average, rounded to two decimal places.
Control versus treatment. One wins.
Tables
ab_results
Columns: result_id INTEGER, test_name TEXT, variant TEXT, user_id INTEGER, metric TEXT, value REAL, created TEXT
| result_id | test_name | variant | user_id | metric | value | created |
|---|---|---|---|---|---|---|
| 237 | new_homepage | treatment_a | 100 | click_rate | 13.7 | 2026-02-02 |
| 274 | pricing_page | treatment_b | 197 | revenue | 27.4 | 2026-03-03 |
| 311 | onboarding_v3 | Control | 294 | bounce | 41.1 | 2026-04-04 |
| 348 | search_algo_b | CONTROL | 391 | time_on_page | 54.8 | 2026-05-05 |
| 385 | rec_engine_v2 | control | 488 | signup | 68.5 | 2026-06-06 |
| 422 | checkout_flow_v2 | treatment_a | 585 | conv_rate | 82.2 | 2026-07-07 |
| 459 | new_homepage | treatment_b | 682 | click_rate | 95.9 | 2026-08-08 |
| 496 | pricing_page | Control | 779 | revenue | 9.6 | 2026-09-09 |
| 533 | onboarding_v3 | CONTROL | 876 | bounce | 23.3 | 2026-01-10 |
| 570 | search_algo_b | control | 973 | time_on_page | NULL | 2026-02-11 |
Expected output
| variant | avg_value |
|---|---|
| CONTROL | 50.55 |
Worked solution and explanation
This is a pick-the-best-group-by-its-mean problem dressed up as an experiment readout. Averaging per label is the easy half. What separates candidates is returning exactly one row, the label with the highest mean, and leaving the labels as logged. The five arms sit within six points of each other (45.00 to 50.55), so every shortcut crowns a different winner. Merge the three spellings of control and that arm drops to 47.96, which hands the crown to the first treatment arm at 49.45. Pick by the single biggest reading and the same arm wins on a 98.7. Only comparing the per-label means returns CONTROL at 50.55.
Walking it
Step 1: Keep the logged label as the grain
Group ab_results on variant alone. Tests and metrics pool together, which is what the readout asks for, and 'Control', 'CONTROL' and 'control' stay three arms because the analyst wants the labels before anyone reconciles them.
Step 2: Average, and let AVG skip the blanks
AVG(value) divides by the rows that actually hold a value, so the 20 empty readings (all on lowercase 'control') drop out instead of dragging that arm down to 22.50. Round in the same expression so avg_value is the number the reader sees.
Step 3: Sort the means and keep the top one
ORDER BY avg_value DESC LIMIT 1 collapses five rows to one. The top two means are 50.55 and 49.45, so the cut never falls between tied rows and no tiebreaker is owed.
SELECT variant, ROUND(AVG(value), 2) AS avg_value
FROM ab_results
GROUP BY variant
ORDER BY avg_value DESC
LIMIT 1One scan, five groups, one row back.
- Labels merged
- Grouping on
LOWER(variant)folds three control spellings into one arm averaging 47.96, and the first treatment arm wins at 49.45. That is a correct answer to a question nobody asked yet. - Labels as logged
- Grouping on
variantkeeps five arms.CONTROLleads at 50.55 and is the one row returned.
Common follow-up questions
- What would you return if two labels tied for the top average? (Tests whether a top-1 cut needs a stated tie rule.)
- How would you report the winner for each test instead of pooled across tests? (Moves from one global top-1 to a top-1 per `test_name`.)
- Once the labels are cleaned, how would you check that the winner is not just noise? (Tests reading counts per arm and spread, not only the mean.)