Короткий ответ
Правдоподобная цифра не доказывает корректность. Начните с формулировки зерна каждой CTE и ожидаемой кардинальности каждого соединения, затем проверьте результат на малом наборе с известным ответом.
Как это работает подробнее
Fan-out выявляют сравнением количества строк, уникальных бизнес-ключей и сумм до и после JOIN. Для связи many-to-many нужна предварительная агрегация или явная bridge-таблица.
Диапазоны времени безопаснее писать как timestamp >= start AND timestamp < end. Это избегает двойного учёта границы и проблем с точностью. SRM проверяют по числу единиц рандомизации, а не по числу событий.
Пример кода
join_cardinality_check.sql
WITH base AS (
SELECT order_id, amount FROM orders
), joined AS (
SELECT b.order_id, b.amount, i.item_id
FROM base AS b
LEFT JOIN order_items AS i USING (order_id)
)
SELECT
(SELECT COUNT(*) FROM base) AS base_rows,
(SELECT COUNT(DISTINCT order_id) FROM base) AS base_orders,
COUNT(*) AS joined_rows,
COUNT(DISTINCT order_id) AS joined_orders,
SUM(amount) AS unsafe_amount
FROM joined;Что использовать на практике
- Требуйте инварианты. Автор заранее пишет, что должно сохраняться и в каких пределах.
- Проверяйте данные, а не стиль. Форматирование автоматизируется, смысл зерна — нет.
- Соразмеряйте глубину. Продакшен-дашборд требует строже ревью, чем одноразовый низкорисковый срез.
Подводные камни
- SUM после one-to-many JOIN. Сумма повторяется для каждой дочерней строки.
- BETWEEN для timestamp. Включённая верхняя граница может дублировать следующий период.
- SRM по событиям. Активные пользователи генерируют разное число событий и искажают размеры групп.