Как посчитать Bounce Rate в SQL

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

Зачем Bounce Rate

Маркетолог жалуется: «Я вливаю бюджет в Performance Max, трафик растёт, а конверсии стоят». Аналитик смотрит — bounce rate новых сессий 85%. Значит, юзер заходит, видит лендинг и сразу уходит. Креатив привлекает «не тех» — деньги тратятся вхолостую.

Bounce rate — быстрый сигнал качества трафика, релевантности контента и UX лендинга. В статье — SQL для расчёта по разным определениям (single-page, engagement-based) и подводные камни.

Что такое Bounce Rate

Bounce rate — доля сессий, в которых пользователь не совершил действий (классически — посмотрел только одну страницу и ушёл).

Bounce rate = sessions с одной page view / all sessions

В GA4 — определение изменилось: bounce = «не engaged», где engaged = сессия >10 сек или с конверсией или с 2+ страницами.

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

Данные: pageviews(session_id, page_url, timestamp).

Классический (single-page-bounce)

WITH session_pages AS (
    SELECT session_id, COUNT(*) AS page_count
    FROM pageviews
    GROUP BY session_id
)
SELECT
    COUNT(*) AS total_sessions,
    SUM(CASE WHEN page_count = 1 THEN 1 ELSE 0 END) AS bounced,
    SUM(CASE WHEN page_count = 1 THEN 1 ELSE 0 END) * 100.0
        / NULLIF(COUNT(*), 0) AS bounce_rate_pct
FROM session_pages;

Простой и понятный. Минус: засчитывает как bounce пользователя, который прочитал лендинг 5 минут и нажал кнопку «Связаться» (она открыла WhatsApp, а не следующую страницу).

Bounce по страницам и источникам

«На какой странице пользователи отваливаются чаще»:

WITH session_info AS (
    SELECT
        session_id,
        MIN(page_url) FILTER (WHERE rn = 1) AS landing_page,
        COUNT(*) AS page_count
    FROM (
        SELECT
            session_id,
            page_url,
            ROW_NUMBER() OVER (PARTITION BY session_id ORDER BY TIMESTAMP) AS rn
        FROM pageviews
    ) ranked
    GROUP BY session_id
)
SELECT
    landing_page,
    COUNT(*) AS sessions,
    SUM(CASE WHEN page_count = 1 THEN 1 ELSE 0 END) * 100.0
        / NULLIF(COUNT(*), 0) AS bounce_rate_pct
FROM session_info
GROUP BY landing_page
HAVING COUNT(*) >= 100
ORDER BY bounce_rate_pct DESC;

HAVING COUNT(*) >= 100 — отсекаем страницы с микро-трафиком (10 заходов и 100% bounce ничего не говорят).

По источникам:

WITH session_summary AS (
    SELECT
        s.session_id,
        s.utm_source,
        COUNT(p.page_url) AS page_count
    FROM sessions s
    LEFT JOIN pageviews p ON p.session_id = s.session_id
    GROUP BY s.session_id, s.utm_source
)
SELECT
    utm_source,
    COUNT(*) AS sessions,
    SUM(CASE WHEN page_count <= 1 THEN 1 ELSE 0 END) * 100.0
        / NULLIF(COUNT(*), 0) AS bounce_rate_pct
FROM session_summary
GROUP BY utm_source
ORDER BY bounce_rate_pct DESC;
Закрепи формулу bounce rate в Карьернике
Запомнить надолго — 5 коротких сессий с задачами на эту тему. Бесплатно
Тренировать bounce rate в Telegram

Engaged-session bounce

Современное определение (GA4-style): bounce = сессия не engaged. Engaged = длительность >10 сек, ИЛИ конверсия, ИЛИ 2+ страницы.

WITH session_metrics AS (
    SELECT
        session_id,
        COUNT(*) AS page_count,
        EXTRACT(EPOCH FROM (MAX(TIMESTAMP) - MIN(TIMESTAMP))) AS duration_sec,
        BOOL_OR(event_type = 'conversion') AS had_conversion
    FROM events
    GROUP BY session_id
)
SELECT
    COUNT(*) AS total,
    SUM(CASE
        WHEN page_count >= 2 THEN 0
        WHEN duration_sec >= 10 THEN 0
        WHEN had_conversion THEN 0
        ELSE 1
    END) AS bounced,
    SUM(CASE
        WHEN page_count >= 2 THEN 0
        WHEN duration_sec >= 10 THEN 0
        WHEN had_conversion THEN 0
        ELSE 1
    END) * 100.0 / NULLIF(COUNT(*), 0) AS bounce_rate_pct
FROM session_metrics;

Этот bounce ниже классического — обычно на 10-30 п.п.

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

Ошибка 1. Считать bounce там, где это SPA

В Single Page App все «страницы» — это hash-навигация. Если не трекаете SPA route changes — все сессии будут single-page и bounce 95%+. Проверьте, что route change шлёт pageview event.

Ошибка 2. Игнорировать time-on-page

Пользователь прочитал блог-пост 8 минут и ушёл — классический bounce, но в реальности это позитивный engagement. Используйте engaged-session bounce или scroll-depth.

Ошибка 3. Сравнивать bounce на лендинге vs внутренней странице

Bounce главной 30% и лендинга 60% — это нормально. Лендинг — пункт назначения для конкретного запроса. Сравнивайте однотипные страницы между собой.

Ошибка 4. Считать total bounce без сегментации

Total bounce 50% — бесполезный показатель. Сегментируйте по источнику, девайсу, типу страницы — это даст actionable signal.

Ошибка 5. Доверять bounce из бота-трафика

Если в данных нет фильтрации ботов — bounce будет завышен (боты часто заходят на одну страницу). Фильтруйте по user_agent или используйте flag is_bot.

Ошибка 6. Bounce vs Exit rate

Bounce — % сессий с одной страницы. Exit — % сессий, где страница была последней. Разные вещи.

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

FAQ

Какой bounce rate считается нормальным?

Контент-блог: 60-80% — норма (пользователь прочитал и ушёл). E-commerce главная: 30-50%. Лендинг с конкретным CTA: 40-60%. SaaS dashboard: 10-20%. Сравнивайте только однотипные страницы.

Bounce 100% — что делать?

Сначала проверьте трекинг. Если SPA — смена маршрутов должна отправлять pageview-событие. Если десктоп-only — нет ли проблем с баннером согласия, блокирующим скрипты на мобильных.

Bounce rate важнее или session duration?

Зависит от страницы. Для блога — duration. Для лендинга с CTA — CR + bounce. Для каталога e-com — depth (страниц за сессию).

Можно ли уменьшить bounce искусственно?

Да, добавив 10-секундный auto-redirect или фейковый «второй pageview». Делать НЕЛЬЗЯ — обманывает себя и команду. Лучше работать с реальным engagement.

Bounce у моб vs десктопа отличаются?

Часто моб выше на 5-15 п.п. — экран меньше, скорость медленнее, юзер торопится. Нормально, сегментируйте.