Красные флаги в SQL: если они есть в запросе, бизнес увидит “рост”, которого не было

Типичный сценарий: аналитик быстро собирает метрику, график “взлетает”, команда бежит праздновать. А через пару дней выясняется, что “рост” был в JOIN, таймзоне или границах окна. Цена ошибки всегда одна: решения принимаются на шуме, доверие к данным падает, а следующий правильный ответ уже никому не нравится.

Почему это происходит: SQL молча разрешает двусмысленность. Он не знает, какая кардинальность “правильная”, где у тебя событие, а где справочник, и что именно означает NULL. Запрос может быть синтаксически идеальным и при этом логически неверным.

Мини-чек-лист ревью, который ловит большую часть багов до релиза:

  1. JOIN и кардинальность
  • Если есть риск many-to-many, ты обязан либо предварительно агрегировать, либо сделать явную дедупликацию ключа. DISTINCT в конце запроса почти всегда маскирует проблему, а не решает её.
  • Любой JOIN без понятного ключа и ожидания “сколько строк станет” — красный флаг.
  1. Фильтры: WHERE против ON
  • Для LEFT JOIN фильтры по правой таблице в WHERE превращают его в INNER JOIN. Если ты хотел “сохранить всех слева” — фильтр должен быть в ON или в отдельном условии после проверки NULL.
  1. Окна и границы периодов
  • Временные окна должны быть определены однозначно: включительно/исключительно. BETWEEN часто подставляет, потому что включает обе границы.
  • Проверь, что дата события, дата загрузки и дата “по бизнесу” — не перепутаны.
  1. Таймзона и “день”
  • Если есть conversion к дате, уточни, в какой таймзоне живёт метрика. “Сутки” в UTC и “сутки” в локальной зоне дают разные ответы, особенно на стыке дней.
  1. NULL-логика
  • NOT IN с NULL может дать пустой результат там, где ты ждёшь исключения. Для анти-джойна часто надёжнее NOT EXISTS.
  • Любое сравнение с NULL без явной обработки (COALESCE, IS NULL) — повод остановиться и проговорить ожидаемое поведение.

В правильной практике запрос читается как контракт: что является зерном данных (grain), где происходит агрегация, какие ключи гарантируют 1 к 1, и почему фильтры стоят именно там. Обычно спорят про “да это же мелочь”, но эти “мелочи” и рисуют самые дорогие графики. Граница применимости простая: чем ближе запрос к продуктовой метрике или эксперименту, тем строже чек-лист, без скидок на скорость.