Бран · продюсер «Хроник Пограничья»
Финансисты принесли сверку: по нашим отчётам магазин заработал больше, чем пришло на счёт. Разница в десятки тысяч. Кто-то ворует или мы что-то считаем дважды?
В прошлом уроке ты чистил строки: пробелы, регистр, тестовые почты. Сегодня грязь другого сорта. Строка сама по себе аккуратная, но её не должно быть вовсе: это копия другой строки. Или наоборот, строка на месте, а значения в ней нет. Учимся находить дубли и пропуски, понимать, откуда они взялись, и решать, что с ними делать.
Курьер звонит дважды
Представь курьера, который принёс посылку, не дождался подписи и через минуту позвонил в дверь снова с той же посылкой. Если бухгалтер считает звонки, посылок у него две. Если смотрит на номер накладной, одна.
С покупками в игре происходит то же. Когда игрок платит, магазин приложений сообщает об этом серверу игры сигналом, его называют вебхук. Если сервер ответил с опозданием, магазин на всякий случай повторяет сигнал, и в purchases появляется вторая строка про ту же покупку. Номер накладной у покупки есть: это receipt_id, номер чека. Он одинаковый у обеих копий.
Найти дубли
Дубликат ищут группировкой по тому, что должно быть уникальным. Каждый чек должен встречаться в таблице один раз, значит группируем по receipt_id и оставляем группы, где строк больше одной:
SELECT receipt_id, count(*) AS copies
FROM purchases
GROUP BY receipt_id
HAVING count(*) > 1;
receipt_id | copies
------------------+--------
and-0c13f52a6e63 | 2
and-1b07143fc37e | 2
and-3a11455c62b3 | 2
...
Таких чеков 28. Посмотри на один из них целиком:
SELECT id, player_id, offer_id, amount_rub, status, created_at
FROM purchases
WHERE receipt_id = 'and-0c13f52a6e63';
id | player_id | offer_id | amount_rub | status | created_at
-----+-----------+----------+------------+--------+------------------------
532 | 243 | 2 | 499.00 | paid | 2026-03-27 17:22:21+00
533 | 243 | 2 | 499.00 | paid | 2026-03-27 17:22:26+00
Игрок тот же, оффер тот же, сумма та же, разница в пять секунд. Это не второй «Мешок гемов», это эхо первого. Самое долгое эхо в базе пришло через 37 секунд.
Сколько всего лишних строк, можно посчитать одной строкой: все строки минус уникальные чеки.
SELECT count(*) - count(DISTINCT receipt_id) AS extra_copies
FROM purchases;
Ответ 28. Здесь совпало с числом чеков-дублей, потому что у каждого ровно одна лишняя копия. Если бы магазин повторил сигнал дважды, чек был бы один, а лишних строк две. Это ловушка третьего квеста.
урок из курса
Урок из курса «Аналитика данных с нуля». Курс покупается один раз и остаётся навсегда: 500 ₽.
Оплата пока не подключена: премиум и курсы открываются промокодом.