Broken Promises Between Tables

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

Domain
SQL
Difficulty
medium
Seniority
L3
Asked in
City and County of San Francisco

Problem

dq_checks holds one row per validation run, and its rule names were logged without any casing standard, so the not-null rule shows up spelled more than one way. Count how many not-null checks failed (passed is 0), returned as a single fail_count.

Every foreign key is a pinky-swear. Count the ones that got broken.

Tables

dq_checks

Columns: check_id INTEGER, tbl_name TEXT, col_name TEXT, rule TEXT, passed INTEGER, fail_pct REAL, run_at TEXT, severity TEXT

dq_checks: 10 of 200 rows
check_idtbl_namecol_namerulepassedfail_pctrun_atseverity
131usersemailunique1NULL2026-02-02 01:00:00high
162productsamountrange1NULL2026-03-03 02:00:00medium
193transactionsstatusformat1NULL2026-04-04 03:00:00low
224eventscreated_atreferential1NULL2026-05-05 04:00:00Critical
255sessionspricefreshness0152026-06-06 05:00:00HIGH
286ad_impressionsregionNot_Null1NULL2026-07-07 06:00:00critical
317ordersuser_idUNIQUE102026-08-08 07:00:00high
348usersemailnot_null1NULL2026-09-09 08:00:00medium
379productsamountunique1NULL2026-01-10 09:00:00low
410transactionsstatusrange0302026-02-11 10:00:00Critical

Expected output

First 1 of 1 rows
fail_count
8

Worked solution and explanation

What this one is really asking

This is a data-cleaning problem wearing a one-line COUNT(*). Anyone can filter passed = 0. The thing that separates candidates is whether they believe the warning about casing: on this table the not-null rule is logged both as 'not_null' and as 'Not_Null'. Write rule = 'not_null' and the query runs, returns a believable number, and is wrong by every row someone logged with capitals.

Step 1: Fold the label before you match it

LOWER(rule) turns 'Not_Null' and 'not_null' into the same string. Do it on the column side, then match against an all-lowercase pattern, and the two spellings stop being two buckets.

Step 2: Match the family, not one spelling

LIKE '%null%' on the folded value catches the not-null rule however it was punctuated, and no other rule on the table contains that substring. Say that check out loud: a pattern is only safe when you have confirmed what else it could catch.

Step 3: Let the flag define failure

passed = 0 is the outcome. fail_pct is NULL on most rows, so a fail_pct > 0 filter silently drops failures that never recorded a percentage.

Failed not-null checks
SELECT COUNT(*) AS fail_count
FROM dq_checks
WHERE LOWER(rule) LIKE '%null%'
  AND passed = 0
Trusts the label
WHERE rule = 'not_null' AND passed = 0 binds to one spelling. Every 'Not_Null' failure vanishes, and nothing warns you.
Normalizes the label
WHERE LOWER(rule) LIKE '%null%' AND passed = 0 folds both spellings first, so the count covers the whole rule.

Common follow-up questions