Cloud Cost Stats by Provider

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
L3

Problem

Allocations in cost_allocs carry no provider, so their category stands in as the label, and they count alongside the billed lines in cloud_costs as one set of costs. An analyst reconciling spend wants each provider with its minimum, maximum and average amount.

Three providers. Three very different bills.

Tables

cloud_costs

Columns: cost_id INTEGER, provider TEXT, svc_name TEXT, region TEXT, amount REAL, acct_id TEXT, bill_date TEXT

cloud_costs: 10 of 200 rows
cost_idprovidersvc_nameregionamountacct_idbill_date
543GCPS3us-west-219.33acct-10012026-02-01
586AzureRDSeu-west-137.16acct-10022026-03-01
629awsLambdaap-southeast-154.99acct-10032026-04-01
672gcpBigQueryus-central172.82acct-10042026-05-01
715AWSCloud Runeurope-west190.65acct-10052026-06-01
758GCPBlob Storageus-east-1108.48acct-10062026-07-01
801AzureCosmos DBus-west-2126.31acct-10072026-08-01
844awsCloudFronteu-west-1144.14acct-10082026-09-01
887gcpPub/Subap-southeast-1161.97NULL2026-01-01
930AWSEC2us-central1179.8acct-10102026-02-01

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 11 rows
providermin_amountmax_amountavg_amount
AWS-268.51784.5797.3000000000001
Azure-181.11731.01824.1395
GCP-224.81713.18787.186
aws-137.41748.84861.0930000000001
compute10017967.618541.44

Worked solution and explanation

What this really is

This is a vertical stack wearing a reporting costume. Two tables that share nothing but an amount column have to become one list of cost lines, and the only real decision is how their rows line up. Anyone can write MIN, MAX and AVG. What separates candidates is seeing three things: there is no key to join on, cost_allocs has no provider so its category must stand in, and every line has to survive the merge. Reach for a join and each amount is counted once per partner it fans out to. Reach for a deduplicating UNION and genuine repeat charges vanish from the average.

Getting there

Step 1: Notice there is nothing to join on

cloud_costs and cost_allocs share svc_name, region and acct_id, but none of them identifies a row on either side. A join on any of them pairs each billing line with every allocation that shares the value, so the same amount lands in the aggregate many times. The ask is one dataset, which means more rows, not wider ones.

Step 2: Make the two shapes line up

A stack matches columns by position, so each branch selects exactly two: a label and amount. cloud_costs contributes provider. cost_allocs has no such column, so category AS provider puts 'compute', 'storage' and the rest in the same slot. The preview gives this away: 'compute' sits in the provider column next to 'AWS'.

Step 3: Keep every line, then summarise once

UNION ALL appends the second branch without comparing rows, which is both correct and the cheaper option. Name it combined, then GROUP BY provider with MIN(amount), MAX(amount) and AVG(amount). Labels stay as stored, so 'AWS' and 'aws' come out as separate rows, exactly as the preview shows.

Stack, then summarise per provider
WITH combined AS (
    SELECT provider, amount FROM cloud_costs
    UNION ALL
    SELECT category AS provider, amount FROM cost_allocs
)
SELECT
    provider,
    MIN(amount) AS min_amount,
    MAX(amount) AS max_amount,
    AVG(amount) AS avg_amount
FROM combined
GROUP BY provider
Deduplicating stack
UNION hashes or sorts the whole combined set to drop rows equal on every selected column. Two separate charges of the same amount under the same label become one, and AVG(amount) drifts toward the values that never repeat.
Append-only stack
UNION ALL streams one branch after the other with no comparison. Every billing line and every allocation line reaches the aggregate exactly once, which is what treating both sources as one dataset means.

Common follow-up questions