SaaS Metrics, Unit Economics & LTV — Real Interview Examples
MRR movement, NRR/GRR, churn, payback period, CAC and LTV. The SaaS analytics patterns that come up in every B2B data and finance interview.
SaaS analytics interviews are the most technically demanding subset of the data interview landscape. The reason isn't the SQL — the queries are mostly straightforward. It's that SaaS metrics have very specific definitions that interviewers will probe relentlessly. Get the definition wrong and your SQL gives the wrong number, even though the syntax is perfect.
This guide is a practical tour of the six SaaS metrics that show up in B2B data and finance interviews. For each: the rigorous definition, the SQL, and the senior-signal moves that earn credit.
The Mental Model
SaaS metrics fall into two layers:
- The revenue layer. MRR, ARR, NRR, GRR, expansion, contraction, churn. These describe the business engine.
- The unit economics layer. CAC, LTV, payback period, LTV/CAC ratio. These describe the unit economics of customer acquisition.
The two layers connect — LTV depends on retention, NRR feeds into ARR growth — but interview questions usually live in one layer or the other. Knowing which layer the question is in tells you which definitions to reach for.
Metric 1 — MRR Movement Waterfall
The question: "Compute the MRR movement waterfall for last month: starting MRR, new, expansion, contraction, churn, ending MRR."
WITH movements AS (
SELECT
movement_type,
SUM(amount) AS total_amount
FROM mrr_movements
WHERE occurred_at >= DATE_TRUNC('month', CURRENT_DATE - INTERVAL '1 month')
AND occurred_at < DATE_TRUNC('month', CURRENT_DATE)
GROUP BY movement_type
),
starting AS (
SELECT SUM(mrr) AS starting_mrr
FROM accounts
WHERE created_at < DATE_TRUNC('month', CURRENT_DATE - INTERVAL '1 month')
AND (churned_at IS NULL OR churned_at >= DATE_TRUNC('month', CURRENT_DATE - INTERVAL '1 month'))
)
SELECT
s.starting_mrr,
COALESCE(MAX(CASE WHEN m.movement_type = 'new' THEN m.total_amount END), 0) AS new_mrr,
COALESCE(MAX(CASE WHEN m.movement_type = 'expansion' THEN m.total_amount END), 0) AS expansion_mrr,
COALESCE(MAX(CASE WHEN m.movement_type = 'contraction' THEN m.total_amount END), 0) AS contraction_mrr,
COALESCE(MAX(CASE WHEN m.movement_type = 'churn' THEN m.total_amount END), 0) AS churn_mrr,
s.starting_mrr
+ COALESCE(MAX(CASE WHEN m.movement_type = 'new' THEN m.total_amount END), 0)
+ COALESCE(MAX(CASE WHEN m.movement_type = 'expansion' THEN m.total_amount END), 0)
- COALESCE(MAX(CASE WHEN m.movement_type = 'contraction' THEN m.total_amount END), 0)
- COALESCE(MAX(CASE WHEN m.movement_type = 'churn' THEN m.total_amount END), 0) AS ending_mrr
FROM starting s
CROSS JOIN movements m
GROUP BY s.starting_mrr;
The senior signal: clarifying that expansion and contraction are signed positively in the mrr_movements table (so contraction is subtracted in the formula). Some companies sign them differently (contraction stored as negative numbers). Asking which convention is in use earns credit.
Metric 2 — Net Revenue Retention (NRR)
The question: "Compute Net Revenue Retention for the cohort of customers we had at the start of last quarter."
NRR = (Starting MRR + Expansion − Contraction − Churn) / Starting MRR. It measures whether your existing customer base grows or shrinks in revenue — independent of new customer acquisition.
WITH q_start AS (
SELECT
account_id,
mrr AS starting_mrr
FROM accounts_history
WHERE snapshot_date = DATE_TRUNC('quarter', CURRENT_DATE - INTERVAL '1 quarter')
),
q_end AS (
SELECT
account_id,
COALESCE(mrr, 0) AS ending_mrr
FROM accounts_history
WHERE snapshot_date = DATE_TRUNC('quarter', CURRENT_DATE) - INTERVAL '1 day'
)
SELECT
SUM(qs.starting_mrr) AS starting_mrr,
SUM(qe.ending_mrr) AS ending_mrr,
ROUND(
SUM(qe.ending_mrr)::numeric / NULLIF(SUM(qs.starting_mrr), 0) * 100,
1
) AS nrr_pct
FROM q_start qs
LEFT JOIN q_end qe ON qe.account_id = qs.account_id;
The senior signal: noting that NRR > 100% means existing customers grew net of churn — the holy grail for B2B SaaS. Best-in-class companies (Snowflake, Datadog) report NRR > 130%. NRR < 90% is a serious retention problem.
Also worth calling out: NRR uses the starting cohort as the denominator. Customers acquired during the period don't count. That's what distinguishes NRR from total revenue growth.
Metric 3 — Gross Revenue Retention (GRR)
The question: "What's the difference between NRR and GRR, and which should we report?"
GRR is NRR's stricter sibling — same numerator setup, but it caps expansion at the starting value. In other words, GRR can't exceed 100%. It measures retention without the cushion of expansion.
WITH q_start AS (
SELECT account_id, mrr AS starting_mrr
FROM accounts_history
WHERE snapshot_date = DATE_TRUNC('quarter', CURRENT_DATE - INTERVAL '1 quarter')
),
q_end AS (
SELECT account_id, COALESCE(mrr, 0) AS ending_mrr
FROM accounts_history
WHERE snapshot_date = DATE_TRUNC('quarter', CURRENT_DATE) - INTERVAL '1 day'
)
SELECT
SUM(qs.starting_mrr) AS starting_mrr,
-- For GRR, cap each account's ending MRR at its starting MRR
SUM(LEAST(qe.ending_mrr, qs.starting_mrr)) AS retained_mrr,
ROUND(
SUM(LEAST(qe.ending_mrr, qs.starting_mrr))::numeric / NULLIF(SUM(qs.starting_mrr), 0) * 100,
1
) AS grr_pct
FROM q_start qs
LEFT JOIN q_end qe ON qe.account_id = qs.account_id;
The senior signal: "NRR with expansion can mask poor base retention. A company can have NRR of 110% even if 30% of customers churned — they just had massive expansion from the survivors. GRR strips that out. Both are useful. Investors usually want both."
Metric 4 — Churn Rate (Logo vs. Dollar)
The question: "Compute our monthly churn rate."
The trap: which churn? Logo churn (% of accounts) and dollar churn (% of MRR) are very different numbers, and they tell different stories.
WITH month_start_accounts AS (
SELECT account_id, mrr
FROM accounts_history
WHERE snapshot_date = DATE_TRUNC('month', CURRENT_DATE - INTERVAL '1 month')
),
month_churned AS (
SELECT account_id
FROM accounts
WHERE churned_at >= DATE_TRUNC('month', CURRENT_DATE - INTERVAL '1 month')
AND churned_at < DATE_TRUNC('month', CURRENT_DATE)
)
SELECT
COUNT(DISTINCT msa.account_id) AS accounts_at_start,
COUNT(DISTINCT mc.account_id) AS accounts_churned,
SUM(msa.mrr) AS mrr_at_start,
SUM(CASE WHEN mc.account_id IS NOT NULL THEN msa.mrr END) AS mrr_churned,
ROUND(COUNT(DISTINCT mc.account_id)::numeric / NULLIF(COUNT(DISTINCT msa.account_id), 0) * 100, 2) AS logo_churn_pct,
ROUND(SUM(CASE WHEN mc.account_id IS NOT NULL THEN msa.mrr END)::numeric / NULLIF(SUM(msa.mrr), 0) * 100, 2) AS dollar_churn_pct
FROM month_start_accounts msa
LEFT JOIN month_churned mc ON mc.account_id = msa.account_id;
The senior signal: logo churn higher than dollar churn = you're losing small accounts (often acceptable). Logo churn lower than dollar churn = you're losing big accounts (alarming). Always report both, and interpret the spread.
Metric 5 — Customer Acquisition Cost (CAC) and Payback
The question: "What's our CAC by channel, and how many months to recoup it?"
WITH acquired AS (
SELECT
channel,
account_id,
created_at,
mrr AS initial_mrr
FROM accounts
WHERE created_at BETWEEN CURRENT_DATE - INTERVAL '6 months'
AND CURRENT_DATE - INTERVAL '3 months'
),
spend AS (
SELECT
channel,
SUM(amount) AS total_spend
FROM marketing_spend
WHERE spend_date BETWEEN CURRENT_DATE - INTERVAL '6 months'
AND CURRENT_DATE - INTERVAL '3 months'
GROUP BY channel
)
SELECT
a.channel,
COUNT(DISTINCT a.account_id) AS accounts_acquired,
s.total_spend,
ROUND(s.total_spend::numeric / NULLIF(COUNT(DISTINCT a.account_id), 0), 2) AS cac,
ROUND(AVG(a.initial_mrr), 2) AS avg_initial_mrr,
-- Payback period in months (assuming ~70% gross margin)
ROUND(
(s.total_spend::numeric / NULLIF(COUNT(DISTINCT a.account_id), 0))
/ NULLIF(AVG(a.initial_mrr) * 0.7, 0),
1
) AS cac_payback_months
FROM acquired a
LEFT JOIN spend s ON s.channel = a.channel
GROUP BY a.channel, s.total_spend
ORDER BY cac_payback_months;
The senior signal: the 0.7 in the denominator is gross margin — SaaS companies typically have 70-80% margins. The right gross margin to use depends on the company. Asking out loud is the right move.
Also: payback period under 12 months = healthy. 12-24 months = acceptable. >24 months = unit economics under pressure.
Metric 6 — LTV and LTV/CAC
The question: "What's the LTV of an average customer, and what's our LTV/CAC ratio?"
Simple LTV formula: LTV = ARPU × Gross Margin × (1 / Churn). The intuition: you make ARPU each period, gross margin is what flows to profit, and the inverse of churn is expected customer lifetime.
WITH metrics AS (
SELECT
AVG(mrr) AS arpu,
0.70 AS gross_margin,
-- Monthly churn (rough estimate from last 90 days)
(
SELECT COUNT(*)::numeric / NULLIF(
(SELECT COUNT(*) FROM accounts WHERE created_at < CURRENT_DATE - INTERVAL '3 months'),
0
)
FROM accounts
WHERE churned_at >= CURRENT_DATE - INTERVAL '3 months'
) / 3.0 AS monthly_churn
FROM accounts
WHERE churned_at IS NULL
)
SELECT
arpu,
gross_margin,
monthly_churn,
ROUND(arpu * gross_margin / NULLIF(monthly_churn, 0), 2) AS ltv,
-- Assuming CAC of $1,200 (replace with actual computation)
1200 AS cac,
ROUND(arpu * gross_margin / NULLIF(monthly_churn, 0) / 1200, 1) AS ltv_cac_ratio
FROM metrics;
The senior signal: LTV/CAC > 3 is healthy. < 1 = unprofitable. The simple formula above breaks down for companies with strong expansion (where NRR > 100%). For those, you need a more sophisticated model that uses NRR-based LTV instead of churn-based LTV.
Calling out the formula's limitation: "This LTV is the simple version. For us, NRR is 115%, which means the simple churn-based formula understates LTV. The right calculation uses cohort revenue persistence — but for back-of-envelope work, this gets us to the right order of magnitude."
The Mistakes That Cost Offers
1. Confusing MRR with ARR. MRR is monthly. ARR is annual (MRR × 12, or directly summed annual contract value). Interviewers will use them interchangeably and watch if you do the conversion silently.
2. Including new logos in NRR. NRR is about the existing cohort. New customers acquired during the period don't count. This is the single most common NRR computation error.
3. Logo churn = dollar churn. Reporting one as if it were the other. They're related but distinct, and the spread is informative.
4. CAC without payback. Saying "CAC is $1,200" without context is meaningless. CAC payback period (or LTV/CAC ratio) is what tells you if the unit economics work.
5. Ignoring gross margin in LTV. LTV without margin is just revenue per customer. The "value" in lifetime value requires the margin adjustment.
What To Practice
SaaS metrics interviews reward precise definitions over creative SQL. Find a SaaS-flavored schema with accounts, mrr_movements, subscriptions, and marketing_spend. For each of the 6 metrics above, write the SQL, then write the definition in plain English, then write what the number tells you about the business.
Six metrics × three artifacts each (SQL + definition + interpretation) = 18 written artifacts. By the end, the SaaS metric vocabulary becomes muscle memory — and you'll never confuse NRR and GRR under interview pressure again.
Learn advanced SQL
Cohort analysis, LTV/CAC, retention curves, A/B testing — FAANG-level analytical SQL.
Learn advanced SQL