Как посчитать Deferred Revenue в SQL

Проверь себя · 1/3разбор после ответа
Что вернёт запрос SELECT AVG(salary) FROM employees;?

Зачем нужен Deferred Revenue

Deferred Revenue (отложенная выручка) — это деньги, которые компания уже получила, но ещё не заработала. Пока сервис не оказан, предоплата висит на балансе как обязательство перед клиентом, а не как выручка. Аналитику в SaaS этот показатель нужен постоянно: он связывает биллинг (сколько денег пришло) с признанной выручкой (сколько мы реально заработали по правилам учёта), и без него нельзя честно посчитать P&L.

Классический пример. В начале года пользователь платит 1200 рублей за годовую подписку. Признать всю сумму как выручку в январе нельзя — сервис ещё не оказан за февраль–декабрь. Выручку признают равномерно, по 100 рублей в месяц. Значит, в конце января признано 100 рублей, а оставшиеся 1100 — это и есть Deferred Revenue: обязательство «отдать» сервис в течение оставшихся 11 месяцев.

На собеседовании SaaS-аналитика этот вопрос любят, потому что он проверяет сразу две вещи: понимаете ли вы разницу между кассовым и начисленным методом учёта, и умеете ли вы пропорционально «размазать» сумму по времени средствами SQL. Ниже разберём и логику, и рабочие запросы.

Формула

В основе всего одна простая идея — отложенная выручка на любой момент времени равна тому, что заплатили, минус то, что уже признали:

Deferred Revenue на момент t = сумма предоплаты − признанная выручка на момент t

Признанная выручка считается пропорционально прошедшей части оплаченного периода: предоплата × (прошедшие дни / всего дней в периоде). Осталось аккуратно переложить это на SQL с учётом того, что период мог ещё не закончиться, а признанная выручка не должна превышать саму предоплату.

Базовый расчёт

Пусть есть таблица subscriptions(user_id, plan_amount, billed_at, period_start, period_end). Считаем признанную и отложенную выручку по каждой активной подписке на текущую дату:

WITH subs AS (
    SELECT
        user_id,
        plan_amount,
        period_start,
        period_end,
        (period_end - period_start)::NUMERIC AS total_days,  -- длина периода в днях
        CURRENT_DATE AS as_of
    FROM subscriptions
    WHERE status = 'active'
)
SELECT
    user_id,
    plan_amount,
    -- Признанная выручка: доля прошедших дней от всего периода × сумма плана.
    -- LEAST не даёт признать больше, чем заплатили; GREATEST отсекает отрицательные дни.
    LEAST(plan_amount,
        plan_amount * GREATEST(0, EXTRACT(EPOCH FROM (LEAST(CURRENT_DATE, period_end) - period_start)) / 86400)::NUMERIC
        / NULLIF(total_days, 0)
    ) AS revenue_recognized,
    -- Отложенная выручка: заплатили минус признали.
    plan_amount - LEAST(plan_amount,
        plan_amount * GREATEST(0, EXTRACT(EPOCH FROM (LEAST(CURRENT_DATE, period_end) - period_start)) / 86400)::NUMERIC
        / NULLIF(total_days, 0)
    ) AS deferred_revenue
FROM subs;

Здесь три защитных приёма, о которых стоит уметь рассказать на собесе. LEAST(CURRENT_DATE, period_end) не даёт признать выручку за пределами оплаченного периода. GREATEST(0, ...) защищает от подписок, которые ещё не начались (отрицательное число дней). NULLIF(total_days, 0) спасает от деления на ноль, если период вырожденный.

Помесячная амортизация

Часто нужна не текущая точка, а помесячный график признания выручки: сколько выручки «упадёт» в каждый календарный месяц. Для этого раскладываем каждую подписку по месяцам и считаем, сколько её дней приходится на конкретный месяц:

WITH monthly_recognized AS (
    SELECT
        s.user_id,
        s.plan_amount,
        s.period_start,
        s.period_end,
        d.month,
        -- Сколько дней подписки попадает в этот месяц: пересечение периода и месяца.
        GREATEST(0, EXTRACT(EPOCH FROM (
            LEAST(s.period_end, d.month + INTERVAL '1 month')
            - GREATEST(s.period_start, d.month)
        )) / 86400) AS days_in_month
    FROM subscriptions s
    CROSS JOIN (
        SELECT generate_series('2026-01-01'::DATE, '2026-12-01'::DATE, INTERVAL '1 month')::DATE AS month
    ) d
    WHERE s.status = 'active'
)
SELECT
    month,
    -- Выручка месяца = сумма плана × доля дней месяца от всей длины периода.
    SUM(plan_amount * days_in_month / NULLIF((period_end - period_start)::NUMERIC, 0)) AS recognized_revenue
FROM monthly_recognized
GROUP BY month
ORDER BY month;

Логика та же пропорция, только знаменатель — вся длина периода, а числитель — дни, попавшие в конкретный месяц. Пересечение периода и месяца задаётся парой LEAST/GREATEST: берём максимум из начал и минимум из концов. Сумма всех помесячных признаний по одной подписке равна её plan_amount — это удобная проверка корректности запроса.

Закрепи формулу deferred revenue в Карьернике
Запомнить надолго — 5 коротких сессий с задачами на эту тему. Бесплатно
Тренировать deferred revenue в Telegram

Общий deferred на дату

Для баланса нужен один агрегат — сколько всего отложенной выручки «висит» на конкретную дату по всем ещё не закрытым подпискам:

WITH all_subs AS (
    SELECT
        plan_amount,
        period_start,
        period_end,
        GREATEST(0, EXTRACT(EPOCH FROM (CURRENT_DATE - period_start)) / 86400) AS days_passed,
        (period_end - period_start)::NUMERIC AS total_days
    FROM subscriptions
    WHERE status = 'active'
      AND period_end > CURRENT_DATE  -- берём только периоды, которые ещё не закончились
)
SELECT
    SUM(plan_amount) AS total_billed,        -- всего выставлено
    SUM(LEAST(plan_amount, plan_amount * days_passed / NULLIF(total_days, 0))) AS recognized_so_far,  -- уже признано
    SUM(plan_amount) - SUM(LEAST(plan_amount, plan_amount * days_passed / NULLIF(total_days, 0))) AS deferred_revenue  -- осталось признать
FROM all_subs;

Итог: deferred_revenue — та самая цифра, которая идёт на баланс как обязательство. Она всегда лежит между нулём и общей суммой биллинга и уменьшается по мере признания выручки.

Частые ошибки

Ошибка 1. Не учитывать разовые платежи. Setup fees и плата за онбординг признаются по другим правилам, чем подписка. Иногда сразу в момент оплаты, иногда равномерно по периоду. Смешивать их с подпиской в одном пропорциональном расчёте нельзя — выделяйте в отдельную статью.

Ошибка 2. Отмена в середине периода. Пользователь отменил подписку в середине месяца. Признанную выручку за прошедшие дни оставляем как есть, а остаток либо возвращаем, либо списываем с deferred. Игнорировать досрочные отмены — значит завысить будущую выручку.

Ошибка 3. Апгрейд в середине периода. Клиент перешёл на более дорогой тариф до конца оплаченного периода. Нужно пересчитать признание: старую часть по старой цене, новую — по новой, с учётом proration. Забыть про это — исказить и выручку, и deferred.

Ошибка 4. Путать подписку и usage-based биллинг. Подписка — это предоплата, у неё есть отложенная выручка. Оплата по факту потребления (usage-based, pay-as-you-go) признаётся сразу по мере использования, deferred у неё почти нет. Применять к ним одну формулу — грубая ошибка.

Ошибка 5. Кассовый метод вместо начисленного. При кассовом методе выручку признают в момент оплаты, при начисленном — равномерно по периоду. Deferred Revenue существует только в начисленном методе, и именно его используют почти все SaaS-компании. Считать по кассе — значит вообще не иметь отложенной выручки.

Связанные темы

FAQ

Deferred Revenue — это обязательство или актив?

Обязательство (liability). Пока сервис не оказан, деньги, полученные вперёд, отражаются на балансе как обязательство «отдать» клиенту оплаченный сервис. По мере оказания услуги обязательство уменьшается, а сумма переходит в выручку.

Как уменьшается Deferred Revenue?

Каждый период (обычно месяц) часть отложенной выручки признаётся как заработанная и переносится из обязательств в выручку. К концу оплаченного периода вся сумма оказывается признанной, и deferred по этой подписке обнуляется.

Чем различаются годовые и месячные подписки по deferred?

Годовая подписка создаёт большой deferred: клиент заплатил за год вперёд, а признаётся выручка помесячно, поэтому обязательство держится высоким почти весь год. У месячной подписки период короткий, deferred минимальный и быстро обнуляется.

Что делать с deferred при возврате средств?

Уменьшаем отложенную выручку на непризнанную часть и корректируем кассу на сумму возврата. Уже признанная за прошедшие дни выручка обычно остаётся признанной — возвращают, как правило, только неотработанный остаток.

Насколько важен deferred для аудита?

Для публичных компаний — критично. Отложенная выручка — одна из ключевых статей при проверке признания выручки и требований SOX-комплаенса. Ошибки в ней напрямую искажают отчётность, поэтому расчёт должен быть воспроизводим и понятен аудитору.


Тренируйте продуктовую аналитику — откройте тренажёр с 1500+ вопросами для собесов.