Net Revenue Retention is the single metric that separates healthy SaaS businesses from ones quietly bleeding out. Unlike MRR or ARR, NRR captures the full picture of what happens after a customer signs up expansion, contraction, and churn all rolled into one number. If your NRR is above 100%, your existing customers are growing you. Below 100%, you're leaking revenue faster than you're filling the bucket.
The problem is that most SaaS teams track NRR badly, or not at all. They pull numbers from their billing system's dashboard, run a spreadsheet calculation once a quarter, or more often just skip it because it's too painful to compute. The data to calculate NRR correctly almost always lives in your database. It's just not easy to get at without writing SQL.
This article walks through exactly how NRR works, how to query it from your database, and how to monitor it automatically so you stop finding out about revenue problems after they've already compounded.
What Net Revenue Retention Actually Measures
NRR measures how much revenue you retained and expanded from your existing customer base over a period, expressed as a percentage.
The formula:
NRR = (Starting MRR + Expansion MRR - Contraction MRR - Churned MRR) / Starting MRR × 100An NRR of 110% means that even if you acquired zero new customers, your revenue would grow 10% from the existing base. An NRR of 85% means you're losing 15% of your revenue from existing customers every period a serious problem that new acquisition has to constantly outrun.
Best-in-class SaaS businesses (Snowflake, Twilio, Datadog in their growth phases) consistently report NRR above 130%. Companies below 100% typically struggle to sustain growth regardless of how aggressive their sales motion is.
Where the Data Lives in Your Database
To calculate NRR, you need three things:
In a typical SaaS database, this usually means querying a subscriptions table (possibly joined with a customers table) and a subscription_events or invoice_history table.
Here's a simplified schema that most SaaS apps will recognise:
subscriptions table
CREATE TABLE subscriptions (
id UUID PRIMARY KEY,
customer_id UUID NOT NULL,
plan_id VARCHAR(50),
mrr NUMERIC(10,2),
status VARCHAR(20), , 'active', 'cancelled', 'paused'
started_at TIMESTAMPTZ,
cancelled_at TIMESTAMPTZ
);If you're using Stripe, your database probably has a mirror of the Stripe objects with similar fields. If you're using a custom billing system, the column names will vary, but the data is there.
Calculating NRR for the Last 30 Days
Here's a query that computes NRR by comparing each customer's MRR at the start vs. the end of a 30-day window:
WITH period_start AS (
, MRR per customer at start of period
SELECT
customer_id,
SUM(mrr) AS mrr_start
FROM subscriptions
WHERE status = 'active'
AND started_at <= NOW() - INTERVAL '30 days'
AND (cancelled_at IS NULL OR cancelled_at > NOW() - INTERVAL '30 days')
GROUP BY customer_id
),
period_end AS (
, MRR per customer at end of period (today)
SELECT
customer_id,
SUM(mrr) AS mrr_end
FROM subscriptions
WHERE status = 'active'
AND started_at <= NOW()
GROUP BY customer_id
),
cohort AS (
, Only include customers who existed at period start
SELECT
ps.customer_id,
ps.mrr_start,
COALESCE(pe.mrr_end, 0) AS mrr_end
FROM period_start ps
LEFT JOIN period_end pe ON ps.customer_id = pe.customer_id
)
SELECT
ROUND(SUM(mrr_end) / NULLIF(SUM(mrr_start), 0) * 100, 2) AS nrr_percent,
ROUND(SUM(mrr_start), 2) AS starting_mrr,
ROUND(SUM(mrr_end), 2) AS ending_mrr,
ROUND(SUM(GREATEST(mrr_end - mrr_start, 0)), 2) AS expansion_mrr,
ROUND(SUM(GREATEST(mrr_start - mrr_end, 0)), 2) AS churned_plus_contraction_mrr
FROM cohort;This query gives you a single NRR number plus the component breakdown. Run it against your production database and you'll see your actual retention health in seconds.
Breaking Down NRR by Plan or Segment
An aggregate NRR number is useful, but it hides where the problem is. A common pattern: enterprise customers expand reliably at 120% NRR while the startup tier churns at 70% NRR, producing a blended number that looks acceptable but masks a serious SMB problem.
Here's how to break it down by plan:
WITH period_start AS (
SELECT
customer_id,
plan_id,
SUM(mrr) AS mrr_start
FROM subscriptions
WHERE status = 'active'
AND started_at <= NOW() - INTERVAL '30 days'
AND (cancelled_at IS NULL OR cancelled_at > NOW() - INTERVAL '30 days')
GROUP BY customer_id, plan_id
),
period_end AS (
SELECT
customer_id,
plan_id,
SUM(mrr) AS mrr_end
FROM subscriptions
WHERE status = 'active'
AND started_at <= NOW()
GROUP BY customer_id, plan_id
),
cohort AS (
SELECT
ps.plan_id,
ps.mrr_start,
COALESCE(pe.mrr_end, 0) AS mrr_end
FROM period_start ps
LEFT JOIN period_end pe ON ps.customer_id = pe.customer_id AND ps.plan_id = pe.plan_id
)
SELECT
plan_id,
ROUND(SUM(mrr_end) / NULLIF(SUM(mrr_start), 0) * 100, 2) AS nrr_percent,
COUNT(*) AS customer_count
FROM cohort
GROUP BY plan_id
ORDER BY nrr_percent DESC;If you're using AI for Database, you can get this breakdown without writing any SQL. Just ask: "Show me net revenue retention by plan for the last 30 days" and the tool translates it, runs it, and returns a table you can share directly with your team.
Tracking NRR as a Monthly Time Series
A single snapshot of NRR tells you where you are. A time series tells you whether you're improving. Here's a query that computes NRR for each of the last 12 months:
WITH months AS (
SELECT generate_series(
date_trunc('month', NOW() - INTERVAL '11 months'),
date_trunc('month', NOW()),
INTERVAL '1 month'
) AS month_start
),
monthly_nrr AS (
SELECT
m.month_start,
ROUND(
SUM(COALESCE(e.mrr, 0)) /
NULLIF(SUM(s.mrr), 0) * 100,
2
) AS nrr_percent
FROM months m
JOIN subscriptions s ON
s.started_at <= m.month_start
AND (s.cancelled_at IS NULL OR s.cancelled_at > m.month_start)
LEFT JOIN subscriptions e ON
e.customer_id = s.customer_id
AND e.started_at <= m.month_start + INTERVAL '1 month'
AND e.status = 'active'
GROUP BY m.month_start
)
SELECT
TO_CHAR(month_start, 'YYYY-MM') AS month,
nrr_percent
FROM monthly_nrr
ORDER BY month_start;This produces a 12-row table you can paste into a chart or drop into a dashboard. In AI for Database, you'd pin this as a self-refreshing dashboard tile every morning, the current month's NRR updates automatically without anyone re-running a query.
Setting Up Automated Alerts When NRR Drops
The real problem with NRR isn't calculating it it's finding out too late that it's deteriorating. A common failure mode: NRR is fine at the quarterly review, then it's a problem at the next one, and by then you've lost three months.
You want an alert the week NRR starts moving in the wrong direction, not the quarter after.
With AI for Database's action workflows, you can define a condition like:
"When this month's projected NRR falls below 95%, send a Slack message to #revenue-alerts with the breakdown by plan."
The system runs your NRR query on a schedule, compares it against the threshold, and fires the alert automatically. No stored procedures, no Lambda functions, no data engineering. You define the condition in plain English and the system handles the rest.
This kind of proactive monitoring is what separates teams that catch retention problems early from teams that notice them on earnings calls.
Common Mistakes When Calculating NRR
Including new customers in the cohort. NRR should only measure what happened to customers who already existed at the start of the period. New signups go into new customer MRR, not NRR.
Using ARR instead of MRR for monthly calculations. ARR is annual; NRR is typically computed monthly. If your contracts are annual, you need to pro-rate to a monthly equivalent.
Ignoring paused subscriptions. A paused subscription isn't churned revenue it's deferred. Counting it as churned overstates churn and understates NRR.
Different start dates for different plans. If you changed plan pricing mid-year, customers on old plans have a different starting MRR than the plan's current price. Make sure you're using the actual MRR from the subscription record, not the plan list price.
Getting Your NRR into a Live Dashboard
The most practical thing you can do after reading this is connect your database, run the NRR query once to verify it against your billing system's reported number, then pin it as a dashboard. When it's a live tile that refreshes daily, NRR stops being a quarterly calculation and starts being a signal you see every morning next to MRR.
Teams that track NRR weekly don't get surprised by retention problems. They catch the early signals a specific plan tier starting to contract, a cohort from a particular acquisition channel churning faster and can act before the numbers compound into a crisis.
Try AI for Database free at aifordatabase.com to connect your database and get your NRR calculated in minutes, no SQL required.