Only Here

A hard SQL interview practice problem on DataDriven. Write the query and run it against a real database, with instant feedback.

Domain
SQL
Difficulty
hard
Seniority
L5
Asked in
Amazon

Problem

Which features in ml_features appear in the 'transactions' source but never in 'page_views' or 'ad_impressions', where two records are the same feature only when both the name and the data type match, and type spellings like 'INT64' and 'int64' count as one type? Each such feature has many transactions records, so return one row per feature with its name, its data type, and the mean recorded value and mean null percentage across those transactions records, both rounded to two decimals.

Exclusive to one source. Missing from the other.

Tables

ml_features

Columns: feat_id INTEGER, feat_name TEXT, dtype TEXT, avg_val REAL, null_pct REAL, updated TEXT, source TEXT

ml_features: 10 of 200 rows
feat_idfeat_namedtypeavg_valnull_pctupdatedsource
141login_countint647.332026-02-02transactions
182cart_valuefloat3214.662026-03-03page_views
223page_dwellstring21.992026-04-04ad_impressions
264click_rateboolean29.2122026-05-05NULL
305days_activeFloat6436.5152026-06-06search_queries
346avg_orderINT6443.8182026-07-07orders
387session_lenfloat6451.1212026-08-08NULL
428scroll_pctint6458.4242026-09-09user_sessions
469bounce_cntfloat3265.7NULL2026-01-10transactions
510user_agestring73302026-02-11page_views

Expected output

First 5 of 6 rows
feat_namedtypeavg_valnull_pct
bounce_cntfloat3265.7NULL
days_activeboolean182.575
login_countint64153.313
page_dwellfloat64240.999
session_lenstring124.151

Worked solution and explanation

What this problem really is

Strip the ML-feature language away and this is a per-(name, type) set difference: keep the transactions rows whose (feat_name, folded dtype) pair shows up in NO other source you care about, then average each survivor's transactions entries down to one line. Anyone can filter to source = 'transactions'. The two things that actually separate candidates are the compound key and the case fold. Match on feat_name alone and you wrongly drop a feature that only collides by name in another source. Compare dtype raw and an 'INT64' sails right past an 'int64' in page_views, so you keep a feature that is not actually exclusive. Both mistakes quietly corrupt the exclusion set, and the row count never tells you it happened.


Break down the requirements

Step 1: Identify transaction-source features

Filter ml_features to rows where source = 'transactions', the source whose exclusive features we want.

Step 2: Normalize the data type

Define feature equality per the prose as (feat_name AND data type). The dtype column carries case-variant spellings of the SAME logical type ('INT64' vs 'int64', 'Float64' vs 'float64'), so normalize with LOWER() so that 'same data type' is judged semantically, not by raw spelling.

Step 3: Exclude features shared with other sources

Apply an anti-join via NOT EXISTS: keep a transactions feature only when NO row with the same feat_name and same case-folded dtype exists in page_views or ad_impressions. Folding case on the comparison side is what stops a differently-cased spelling in the other sources from sneaking past the exclusivity check.

Step 4: Average each feature's transactions entries

Collapse to one row per feature over the transactions records only. Because a feature is logged many times in transactions, GROUP BY feat_name and the folded dtype, then report ROUND(AVG(avg_val), 2) and ROUND(AVG(null_pct), 2) across those transactions rows. AVG ignores NULLs, so a feature whose null_pct is entirely NULL reports NULL rather than 0. This is why the sample's two login_count entries, spelled 'int64' and 'INT64', collapse under one 'int64' label, while bounce_cnt's lone NULL null_pct rolls up to NULL.


The solution

Set difference with compound key exclusion
SELECT
  f.feat_name,
  LOWER(f.dtype) AS dtype,
  ROUND(AVG(f.avg_val), 2) AS avg_val,
  ROUND(AVG(f.null_pct), 2) AS null_pct
FROM ml_features f
WHERE f.source = 'transactions'
  AND NOT EXISTS (
    SELECT 1
    FROM ml_features o
    WHERE o.feat_name = f.feat_name
      AND LOWER(o.dtype) = LOWER(f.dtype)
      AND o.source IN ('page_views', 'ad_impressions')
  )
GROUP BY f.feat_name, LOWER(f.dtype)
ORDER BY f.feat_name, dtype

Common follow-up questions