Use CasesAISQLdashboards

How to Track Net Revenue Retention (NRR) from Your Database

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 pi...

Marcus Chen· Solutions EngineerApril 12, 20267 min read

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 × 100

An 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:

  • Subscription records which customer is on which plan, at what MRR, and when that changed
  • Change events upgrades, downgrades, cancellations, and reactivations with timestamps
  • Customer identifiers to group subscriptions to the same account
  • 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.

    Ready to try AI for Database?

    Query your database in plain English. No SQL required. Start free today.