Data Repo Fix Commits
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
Problem
Our fix tally lists some engineers twice, once per way their name was capitalized, and misses commits written as 'Fix' instead of 'fix'. Rebuild it from repo_commits: per repository and author, the number of commits whose message mentions 'fix', with each person under a single lowercase name.
How many commits start with 'fix'?
Tables
repo_commits
Columns: commit_id INTEGER, repo_name TEXT, author TEXT, message TEXT, added INTEGER, removed INTEGER, commit_at TEXT
| commit_id | repo_name | author | message | added | removed | commit_at |
|---|---|---|---|---|---|---|
| 3061 | backend-api | bob | Add unit tests | 17 | 7 | 2026-02-02 01:09:00 |
| 3122 | infra | charlie | Refactor auth module | 34 | 14 | 2026-03-03 02:18:00 |
| 3183 | ml-pipeline | charlie | Update deps | 51 | 21 | 2026-04-04 03:27:00 |
| 3244 | mobile-app | dana | Improve perf | 68 | 28 | 2026-05-05 04:36:00 |
| 3305 | data-platform | frank | Add caching layer | 85 | 35 | 2026-06-06 05:45:00 |
| 3366 | frontend | Alice | Fix typo in readme | 102 | 42 | 2026-07-07 06:54:00 |
| 3427 | backend-api | Bob | NULL | 119 | 49 | 2026-08-08 07:03:00 |
| 3488 | infra | alice | WIP | 136 | 56 | 2026-09-09 08:12:00 |
| 3549 | ml-pipeline | bob | 153 | 63 | 2026-01-10 09:21:00 | |
| 3610 | mobile-app | charlie | Add search endpoint | 170 | 70 | 2026-02-11 10:30:00 |
Expected output
| repo_name | author | fix_count |
|---|---|---|
| backend-api | bob | 2 |
| backend-api | dana | 4 |
| backend-api | frank | 4 |
| data-platform | bob | 6 |
| data-platform | dana | 2 |
Worked solution and explanation
What this is really testing
This is a case-insensitive pattern count with a dirty grouping key, dressed up as a bug-fix tally. Anyone can write the COUNT(*) and the GROUP BY. The trick is that capitalization is noise on BOTH sides: every fix message in repo_commits starts with a capital ('Fix login bug', 'Fix flaky test'), and the same engineers were typed as 'alice' and 'Alice', 'bob' and 'Bob'. Fold only one side and the tally either comes back empty or shows two people where there is one, with their fixes split between the rows.
Step 1: Fold the message before matching
LOWER(message) LIKE '%fix%' turns 'Fix' and 'FIX' into 'fix' before the comparison. The wildcards on both sides are what 'mentions fix' means, so 'hotfix' or 'fixed' would count too. A NULL or empty message never matches and drops out without a guard.
Step 2: Fold the author before counting
Group on LOWER(author), not author, and return that folded value as author. On the seed this merges frontend's 'Alice' (4 fixes) with 'alice' (2) into one row of 6, and data-platform's 'bob' and 'Bob' the same way: 15 raw groups become 13 people.
Step 3: Count rows per repository and person
COUNT(*) per (repo_name, LOWER(author)) is the whole metric: every row that survives the filter is one fix commit. No order is asked for, so none is imposed.
The solution
SELECT repo_name,
LOWER(author) AS author,
COUNT(*) AS fix_count
FROM repo_commits
WHERE LOWER(message) LIKE '%fix%'
GROUP BY repo_name, LOWER(author)- Raw author
GROUP BY repo_name, authorreturns 'Alice' with 4 and 'alice' with 2 on frontend as two rows. The manager reads two half-busy engineers.- Folded author
GROUP BY repo_name, LOWER(author)returns one 'alice' row with 6. One person, one row, the full count.
Common follow-up questions
- What if 'fix' appears inside a word like 'prefix' or 'suffix'? ('%fix%' matches those too; anchoring the match to a word boundary with a regular-expression function narrows it.)
- Two author values carry stray spaces (' charlie', 'dana '). Would `TRIM` change this answer? (Not on this data, since neither is on a fix commit, but folding with `LOWER(TRIM(author))` is the safer habit.)
- How would you show each repository's fix commits as a share of all its commits? (A conditional `SUM(CASE WHEN ... THEN 1 ELSE 0 END)` over an unfiltered scan, divided by `COUNT(*)`.)