Noisiest Tables by DQ Failures

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
Google

Problem

Every row of dq_checks is one run of a quality rule against a table's column, recording whether the check passed and, for a failure, the percentage of rows that broke the rule. Show which tables the failures pile up on, with how many of each table's checks failed and the worst failure percentage among those failed checks, most failed checks first.

The tables that fail the most checks.

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 5 of 7 rows
tbl_namefailure_countmax_fail_pct
users655
transactions640
sessions625
products695
orders410

Worked solution and explanation

What this really is

This is a filtered rollup dressed up as data quality monitoring. Everyone writes the GROUP BY tbl_name; what separates candidates is where the failure scope goes and which column defines a failure. dq_checks holds every run, passing and failing, and each table here ran 28 or 30 checks while only 4 or 6 of them failed. Group before you scope and failure_count becomes a count of checks run, so users and products top the list just for running the most checks.

Building it

Step 1: Scope to failed checks first

WHERE passed = 0 runs before grouping, so every aggregate after it sees failures only. That one predicate turns COUNT(*) into a failure count and keeps a table with no failures from forming a group at all.

Step 2: Group on tbl_name alone

One row per table. col_name, rule and severity are not part of the grain; adding any of them splits a table's failures across several rows.

Step 3: Count and take the worst in one pass

COUNT(*) counts the failed checks and MAX(fail_pct) takes the worst percentage among them. Both read the same filtered rows, so they cannot disagree about which checks they describe.

Step 4: Sort by failure volume

ORDER BY failure_count DESC puts the noisiest tables first. Six tables tie at 6 failures; the question only asks for volume order, so their order among themselves is free.

Filter, group, aggregate, sort
SELECT
    tbl_name,
    COUNT(*) AS failure_count,
    MAX(fail_pct) AS max_fail_pct
FROM dq_checks
WHERE passed = 0
GROUP BY tbl_name
ORDER BY failure_count DESC
Count everything, branch inside
SUM(CASE WHEN passed = 0 THEN 1 ELSE 0 END) gets the count right, but every table still forms a group, so a table with no failures would come back with a zero. MAX(fail_pct) also ranges over passing rows: correct here only because they store NULL or 0.0.
Filter, then aggregate
WHERE passed = 0 scopes both aggregates at once, drops tables without failures, and sends only failing rows into the grouping.

Common follow-up questions