Повторное обращение в SQL: считаем заявки, а не строки журнала

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

Для списка клиентов с повторными обращениями сначала нужна таблица, где одна строка означает одну заявку. Только затем можно считать заявки по клиентам. Это простое разделение защищает и от технических повторов доставки, и от длинной истории работы над одним вопросом.

Выбрать момент, который определяет период

Слово «за неделю» допускает разные определения. Заявку могли создать в воскресенье, а закрыть во вторник. Для отчёта о входящем потоке возьмём момент создания. Обновление статуса на следующей неделе не должно переносить заявку между периодами.

В примере источник уже выделил событие created. В реальной системе полезно проверить, что идентификатор заявки сохраняется после назначения и закрытия. Если поддержка склеивает обращения, понадобятся правила для исходного и итогового номера. Считать все события только потому, что в них есть ticket_id, недостаточно.

Сначала восстановить заявки

WITH events(ticket_id, client_id, kind, happened_at) AS (
  VALUES (101, 7, 'created', '2026-10-05'),
         (101, 7, 'created', '2026-10-05'),
         (101, 7, 'assigned', '2026-10-06'),
         (102, 7, 'created', '2026-10-08'),
         (103, 8, 'created', '2026-10-09'),
         (103, 8, 'closed', '2026-10-10'),
         (104, 7, 'created', '2026-10-12')
), tickets AS (
  SELECT ticket_id, client_id, MIN(happened_at) AS created_at
  FROM events
  WHERE kind = 'created'
  GROUP BY ticket_id, client_id
)
SELECT client_id, COUNT(*) AS tickets_count
FROM tickets
WHERE created_at >= '2026-10-05'
  AND created_at < '2026-10-12'
GROUP BY client_id
HAVING COUNT(*) >= 2
ORDER BY client_id;

Запрос вернёт клиента 7 и число 2. Повторная доставка created для заявки 101 схлопнулась. Назначение исполнителя не стало новым обращением. Заявка 104 относится уже к следующему периоду. Клиент 8 не проходит порог, хотя у него две строки истории.

WHERE во внешнем запросе выбирает заявки нужной недели, а HAVING оставляет клиентов после подсчёта. Если попробовать отобрать клиентов по числу сырых событий раньше, смысл показателя снова изменится.

Проверить, что ключ действительно однозначен

Здесь предполагается, что одна заявка принадлежит одному клиенту. Перед большим отчётом найдите ticket_id, у которых больше одного уникального client_id. Такие записи требуют решения: это исправление клиента в источнике, объединение организаций или ошибка интеграции. Молчаливый GROUP BY по двум полям превратит спорную заявку в две.

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

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

От такого запроса к полноценной аналитике

Потренируйте разделение фильтра строк и фильтра групп в задачах на GROUP BY и HAVING. Меняйте число событий у одной заявки и проверяйте, должен ли меняться ответ.

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

Продолжить на практике