Условие
В programmatic-рекламе показы (impressions) выводятся на сайтах через DSP, кликом мы фиксируем переход на сайт рекламодателя, конверсией — депозит. Даны три таблицы:
impressions
impression_id: stringurl_address: stringuser_id: stringrequest_country: stringtracking_type: string(cookie/fingerprint)dynamic_display: booleandynamic_display_variables: stringrequest_browser_name: stringtimestamp: date
clicks
impression_id: intuser_id: inttimestamp: string
conversions
conversion_id: stringuser_id: stringdval: integer(deposit value)curr: stringtimestamp: date
Посчитайте CTR (%) по показам в стране Austria.
Решение
Подход
CTR = clicks / impressions × 100%. «Кликом» считаем такой clicks.impression_id, который ссылается на показ в Austria. Самый чистый способ — LEFT JOIN от impressions:
Реализация (PostgreSQL)
SELECT
100.0 * COUNT(c.impression_id)::numeric / COUNT(*)::numeric AS ctr_pct
FROM impressions i
LEFT JOIN clicks c
ON c.impression_id::text = i.impression_id
WHERE i.request_country = 'Austria';Альтернатива — через подзапрос:
SELECT
100.0 *
(SELECT COUNT(*)
FROM clicks c
JOIN impressions i ON i.impression_id = c.impression_id::text
WHERE i.request_country = 'Austria')::numeric
/
NULLIF((SELECT COUNT(*) FROM impressions WHERE request_country = 'Austria'), 0) AS ctr_pct;Анализ результата
- Ключевая проверка — типы данных:
clicks.impression_idобъявлен какint, а вimpressions—string. На реальной БД это либо нужно кастовать (::text), либо оба типа должны совпадать. Если у части импрешенов id не numeric —::intразвалится. LEFT JOINпротивINNER JOIN: приINNER JOINмы потеряем показы без кликов и не сможем посчитать знаменатель. НуженLEFT.- Если у одного
impression_idможет быть несколько кликов (двойной клик, ретрай), числитель раздуется. Стандарт:COUNT(DISTINCT c.impression_id).
Усиленная версия
SELECT
100.0 * COUNT(DISTINCT c.impression_id)::numeric
/ NULLIF(COUNT(DISTINCT i.impression_id), 0) AS ctr_pct
FROM impressions i
LEFT JOIN clicks c
ON c.impression_id::text = i.impression_id
WHERE i.request_country = 'Austria';Подводные камни
- Несовместимые типы id.
intvsstring— ловите на проде черезCASTи проверяйте, что нет non-numeric id. - Двойные клики.
COUNT(DISTINCT c.impression_id)— стандарт; иначе CTR > 100% возможен. COUNT(c.impression_id)vsCOUNT(*). Первый считает не-NULL послеLEFT JOIN; второй посчитал бы все строки (включая показы без кликов). Используем разные подсчёты.- NULL-страна. Если
request_countryможет быть NULL — не учтётся вWHERE. ОК, но стоит явно отметить. - Целочисленное деление. В строгих диалектах
1/2 = 0. Кастуем вnumericили умножаем на100.0. - Период. Часто CTR считают за конкретное окно (
WHERE timestamp BETWEEN ...); запрос без фильтра — за всё время.
Эталонный ответ
SELECT 100.0 * COUNT(DISTINCT c.impression_id)::numeric
/ NULLIF(COUNT(DISTINCT i.impression_id), 0) AS ctr_pct
FROM impressions i
LEFT JOIN clicks c ON c.impression_id::text = i.impression_id
WHERE i.request_country = 'Austria';LEFT JOIN сохраняет знаменатель, DISTINCT защищает от двойных кликов, фильтр по стране — в WHERE.