DQ Score Spread

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
L4
Asked in
Box

Problem

How far apart are the best and worst monitored tables in the dq_checks audit log? The analyst reviewing it scores each table by adding up the fail percentages of all of its checks.

The spread in data quality scores.

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
dq_spread
450

Worked solution and explanation

What this really is

This is a spread of group totals dressed up as a data-quality report. The figure being compared is not a column of dq_checks. It is a sum per tbl_name, so the answer is the extremes of an aggregate. Anyone can write SUM(fail_pct). What separates candidates is noticing that the highest and lowest are taken over table totals, not over checks. Skip that step and MAX(fail_pct) - MIN(fail_pct) returns 95, the range of single checks, instead of 450, the gap between 'events' at 480 and 'orders' at 30.

Walking to the answer

Step 1: Reduce the checks to one total per table

GROUP BY tbl_name with SUM(fail_pct) turns the 200 rows of dq_checks into seven table totals. SUM skips the NULL fail_pct on passing checks, which is exactly the business rule: a check that passed adds nothing.

Step 2: Take the extremes over the totals

Wrap that result in a derived table and compute MAX(total_fail) - MIN(total_fail) over its seven rows. The outer level has no GROUP BY because the whole set of tables is one group, so the answer comes back as a single dq_spread row.

Totals per table, then the gap between the extremes
SELECT MAX(total_fail) - MIN(total_fail) AS dq_spread
FROM (
    SELECT tbl_name, SUM(fail_pct) AS total_fail
    FROM dq_checks
    GROUP BY tbl_name
) t
Spread over checks
SELECT MAX(fail_pct) - MIN(fail_pct) FROM dq_checks treats every check as a peer. On this data it returns 95: one check failing at 95 percent against one at 0.
Spread over tables
The derived table sums each tbl_name first, so 'events' carries 480 and 'orders' carries 30. The outer MAX and MIN then return 450, the gap the report is actually about.

Common follow-up questions