Shadow Spend

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

Problem

Billed costs sit in cloud_costs and internal allocations in cost_allocs: what is the total spend for each region and service pairing when a line item (the same region, service and amount) recorded more than once anywhere in the two counts only once? A line item with no amount recorded is not spend, so it never puts a pairing on the list.

Region by region. Service by service. Where does the money go?

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 42 rows
regionsvc_nametotal_spend
ap-south-1Lambda48668.23
ap-south-1RDS170775.36
ap-south-1S3250
ap-southeast-1CloudFront165307.56
ap-southeast-1Cosmos DB1854.52

Worked solution and explanation

What this really is

Strip the FinOps costume and this is a set union with two decisions hiding in the wording. Two feeds describe the same spend, and the same charge can be written down more than once, so the operator that collapses identical rows (UNION) versus the one that keeps every copy (UNION ALL) is the whole ballgame: reach for UNION ALL out of the usual speed reflex and every charge recorded twice is summed twice. The second decision is quieter. A slice of the billed feed carries no amount at all, and if you let those rows through, GROUP BY region, svc_name with SUM(amount) mints a pairing whose total is NULL: a service that looks reported but carries no spend. Everyone can stack two SELECTs. The tell is hearing 'once' demand UNION and dropping the unpriced rows before they become a group.


Break down the requirements

Step 1: Union the two feeds

Select region, svc_name, amount from both cloud_costs and cost_allocs, joined with UNION (not UNION ALL) so a line item recorded more than once collapses to a single row.

Step 2: Drop the unpriced rows

Apply amount IS NOT NULL to the combined set. A row with no amount would otherwise survive the merge and, once grouped, surface as a pairing whose SUM(amount) is NULL: a service that reads as reported yet carries no cost.

Step 3: Aggregate

GROUP BY region, svc_name with SUM(amount) to get one total per pairing.


The solution

Deduplicated union with the unpriced rows filtered
WITH combined AS (
    SELECT region, svc_name, amount FROM cloud_costs
    UNION
    SELECT region, svc_name, amount FROM cost_allocs
)
SELECT region, svc_name, SUM(amount) AS total_spend
FROM combined
WHERE amount IS NOT NULL
GROUP BY region, svc_name

Common follow-up questions