Deploy Outcomes by Service

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
L4
Asked in
City and County of San Francisco

Problem

Nobody standardized deploy_logs: one service shows up in svc_name under different casings and with stray spaces, any status that mentions success counts as a success, and 'failed' and 'rolled_back' count whatever their casing. An on-call engineer wants a scorecard with one row per service and its successful, failed and rolled-back deploy counts side by side.

Success, failure, rollback - side by side.

Tables

deploy_logs

Columns: log_id INTEGER, svc_name TEXT, version TEXT, env_name TEXT, status TEXT, deploy_at TEXT, dur_secs INTEGER, author TEXT

deploy_logs: 10 of 200 rows
log_idsvc_nameversionenv_namestatusdeploy_atdur_secsauthor
131payment-apiv1.0.2stagingfailed2026-02-02 01:11:0023bob
162user-svcv1.1.0devrolled_back2026-03-03 02:22:0036charlie
193search-api2.0.0canaryin_progress2026-04-04 03:33:0049dana
224gatewayv2.1.0ProductionSuccess2026-05-05 04:44:0062eve
255notif-svc2.1.1STAGINGFAILED2026-06-06 05:55:0075frank
286analyticsv3.0.0-rc1productionsuccess2026-07-07 06:06:0088Alice
317ml-servingv1.0.3stagingfailed2026-08-08 07:17:00NULLBOB
348auth-svcv1.0.1devRolled_Back2026-09-09 08:28:00114alice
379Payment-APIv1.0.2canaryin_progress2026-01-10 09:39:00127bob
410user-svcv1.1.0ProductionSuccess2026-02-11 10:50:00140charlie

Expected output

First 5 of 8 rows
svc_namesuccess_countfailed_countrolled_back_count
analytics1608
auth-svc1608
gateway1808
ml-serving0160
notif-svc0160

Worked solution and explanation

A pivot hiding behind two dirty columns

This is a pivot dressed up as an on-call scorecard. A long status column has to become three fixed outcome columns with one row per service, and the catch is that neither column you touch was ever cleaned. Anyone can write three SUM(CASE ...) expressions. What separates candidates is normalizing before comparing: test the raw status = 'failed' and every 'FAILED' row counts as zero, so failures are undercounted. Group on the raw svc_name and 'payment-api' comes back as four rows, so the shape is wrong too.


Build it in three moves

Step 1: Clean the grouping key

"One row per service" is only true if one service has one spelling. The seed stores ' payment-api', 'payment-api ', 'Payment-API' and 'payment-api'. Select and group by the same LOWER(TRIM(svc_name)) expression; skip either function and a padded or capitalised copy survives as its own row.

Step 2: Lower status inside every branch

Each CASE compares LOWER(status), so 'FAILED' and 'Rolled_Back' land in their columns instead of vanishing. Lowering only the branch you expect to be dirty is how a rollback quietly goes missing.

Step 3: Pivot with three conditional sums

Success uses LIKE '%success%' because any label mentioning success counts; failed and rolled-back are exact matches on the lowered label. A row like 'in_progress' matches no branch and adds 0 to all three.

Normalize, then pivot
SELECT
  LOWER(TRIM(svc_name)) AS svc_name,
  SUM(CASE WHEN LOWER(status) LIKE '%success%' THEN 1 ELSE 0 END) AS success_count,
  SUM(CASE WHEN LOWER(status) = 'failed' THEN 1 ELSE 0 END) AS failed_count,
  SUM(CASE WHEN LOWER(status) = 'rolled_back' THEN 1 ELSE 0 END) AS rolled_back_count
FROM deploy_logs
GROUP BY LOWER(TRIM(svc_name))

One grouped pass over deploy_logs, cleaning the key and the label inline.

Long format
GROUP BY svc_name, status returns one row per raw pair. Every spelling of every service and every outcome is its own row, so the reader has to reassemble the scorecard by hand.
Pivoted
GROUP BY the cleaned svc_name with three conditional sums returns exactly one row per service and three fixed columns, whatever spellings the data holds.
The long-format trap
SELECT svc_name, status, COUNT(*) AS deploys
FROM deploy_logs
GROUP BY svc_name, status

Raw keys, raw labels: fragmented rows and split counts.


Common follow-up questions