Creatures of Habit

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
Google

Problem

An analyst auditing cost_allocs finds one team logged under several letter casings ('DATA-ENG' and 'data-eng' are the same team), and the SQS and EC2 lines are billed through a separate ledger, so they stay out of the audit entirely. Going alphabetically through the teams left with more than 10 allocations, report each team's most-used service and how many allocations landed there, taking the alphabetically first service when several share the top count, along with how many different services the team spreads across.

Every big team has one service it can't quit.

Tables

cost_allocs

Columns: alloc_id INTEGER, team_name TEXT, svc_name TEXT, region TEXT, amount REAL, period TEXT, acct_id TEXT, category TEXT

cost_allocs: 10 of 200 rows
alloc_idteam_namesvc_nameregionamountperiodacct_idcategory
129data-engS3us-west-283.212026-02acct-101storage
158ml-teamRDSeu-west-1156.422026-03acct-102network
187frontendLambdaap-south-1229.632026-04acct-103database
216backendBigQueryus-central1-120.52026-05acct-104ml
245devopsCloudFronteurope-west1376.052026-06acct-105other
274securityEKSap-southeast-1449.262026-07acct-106compute
303mobileSQSeu-central-1522.472026-08acct-107storage
332PlatformEC2us-east-1595.682026-09NULLnetwork
361DATA-ENGS3us-west-2668.892026-01acct-109database
390platformRDSeu-west-1742.12026-02acct-110ml

Expected output

First 5 of 8 rows
team_namesvc_namecntsvc_count
backendBigQuery36
data-engLambda66
frontendCloudFront36
mobileEKS36
platformRDS66

Worked solution and explanation

What this really is

Strip off the cloud-cost costume and this is a per-group argmax reported next to a per-group breadth. For each qualifying team you need the mode of svc_name (the service it lands on most) and, separately, how many DIFFERENT services it touches. The obvious read is a global leaderboard with ORDER BY count then LIMIT 1, which hands back one service for the entire company and silently ignores the per-team requirement. And the two counts pull in opposite directions: 'most often' is a count of rows, while breadth is a count of distinct services, so reusing one for the other quietly inflates the answer.


Stage one: who makes the cut

Step 1: Fold the casings, then filter teams by volume

Before anything is counted, collapse team_name with LOWER(team_name) so 'DATA-ENG' and 'data-eng' become one team. In the same pass, drop the 'SQS' and 'EC2' lines with a NOT IN list, because they belong to another ledger and must not count toward volume, the favorite service, or breadth. Skip the fold and the merged team's allocations split across two rows, each half may miss the bar, and the top service is picked from half the evidence. Then group by the folded name and keep only teams whose remaining allocation count clears the bar. That test depends on an aggregate over the whole group, so it belongs after grouping.

Stage two: the habit and the breadth

Step 2: Two counts, kept apart

For the qualifying teams, count rows per (team, service) to get each service's within-team frequency. Separately, count DISTINCT services per team to get breadth. COUNT(*) here answers 'how often'; COUNT(DISTINCT svc_name) answers 'how many different'. A team with twenty allocations spread over eight services has a frequency of twenty rows but a breadth of eight, and swapping one for the other reports breadth as twenty.

Step 3: Pick the top service per team

Restrict the per-service counts to the qualifying teams with team_name IN (SELECT team_name FROM big_teams), then number the services inside each team from most-allocated down and keep the top one. ROW_NUMBER partitioned by team gives exactly one winner per team; a global ORDER BY plus LIMIT would give exactly one winner total. The tie-break on svc_name inside the ORDER BY makes the pick deterministic when several services share the top count.

Global mode (wrong)
Count per service across all qualifying teams, ORDER BY count DESC LIMIT 1. Returns one service for the whole company and drops every team but the accidental winner.
Per-team mode (right)
Count per (team, service), then ROW_NUMBER() OVER (PARTITION BY team ORDER BY count DESC, svc_name). One row per team, each showing that team's own favorite plus its breadth.

The solution

Folded team names, excluded ledger lines, volume filter, per-team mode, and a separate breadth
WITH team_rows AS (
  SELECT LOWER(team_name) AS team_name, svc_name
  FROM cost_allocs
  WHERE svc_name NOT IN ('SQS', 'EC2')
),
big_teams AS (
  SELECT team_name
  FROM team_rows
  GROUP BY team_name
  HAVING COUNT(*) > 10
),
team_breadth AS (
  SELECT team_name, COUNT(DISTINCT svc_name) AS svc_count
  FROM team_rows
  GROUP BY team_name
),
svc_counts AS (
  SELECT team_name, svc_name, COUNT(*) AS cnt
  FROM team_rows
  WHERE team_name IN (SELECT team_name FROM big_teams)
  GROUP BY team_name, svc_name
),
ranked AS (
  SELECT team_name, svc_name, cnt,
         ROW_NUMBER() OVER (PARTITION BY team_name ORDER BY cnt DESC, svc_name) AS rn
  FROM svc_counts
)
SELECT r.team_name, r.svc_name, r.cnt, b.svc_count
FROM ranked r
JOIN team_breadth b ON r.team_name = b.team_name
WHERE r.rn = 1

LOWER(team_name) merges the casings before any count, the NOT IN list removes the separately billed lines, the IN subquery keeps only qualifying teams, and COUNT(DISTINCT svc_name) keeps breadth honest.


Common follow-up questions