🧠 Почему аналитик дважды подумает, прежде чем писать IFNULL, ROUND, CAST, NOW()
и что потом с этим делать, когда всё сломалось
Потому что эти штуки:
— тихо маскируют ошибки (NULL → 0, и привет кривой отчёт)
— ломают агрегации (ROUND в строках → минус доверие к итогам)
— ведут к нестабильности (NOW() в проде ≠ NOW() в dev)
— создают магию, которую потом никто не может воспроизвести
Вместо встроенных хакающих функций —
нормальные SQL-функции с понятной логикой, версионированием, комментариями и тестами.
В посте:
• разбор, почему эти функции опасны
• читаемые примеры
• как писать надёжный SQL
💸 Поддержать канал
👉 Senior Data Analyst | #sql
и что потом с этим делать, когда всё сломалось
Потому что эти штуки:
— тихо маскируют ошибки (NULL → 0, и привет кривой отчёт)
— ломают агрегации (ROUND в строках → минус доверие к итогам)
— ведут к нестабильности (NOW() в проде ≠ NOW() в dev)
— создают магию, которую потом никто не может воспроизвести
"Лишь бы работало" — не подход.
Вместо встроенных хакающих функций —
нормальные SQL-функции с понятной логикой, версионированием, комментариями и тестами.
В посте:
• разбор, почему эти функции опасны
• читаемые примеры
• как писать надёжный SQL
💸 Поддержать канал
👉 Senior Data Analyst | #sql
👍7❤1
▪️Что не так?
Базовые SQL-функции кажутся удобными.
Но если применять их бездумно — это путь к тихим багам, кривым отчётам и вечному “а почему здесь 0?”
▪️Где сыпется чаще всего:
COALESCE / IFNULL
Выглядит мило:
Но скрывает NULL и мешает понять, откуда дыра.
В результате ты "лечишь" витрину, которая уже умерла.
Правильно:
→ Комментируй замену:
→ Лучше —
Кейс: retention по оплатам. Пользователь не платил → NULL → подставили 0 → метрика выросла. Радоваться рано.
▪️ ROUND(..., 2)
Если ты округляешь до суммы — всё норм.
Если до — всё грустно.
Раннее округление:
→ копится погрешность
→ сумма строк != total
→ отчёт бьётся по швам
Правильно:
→ округляем после агрегации
→ лучше в обёртке типа
▪️NOW() / CURRENT_TIMESTAMP
Самая коварная “удобная” функция.
Каждый вызов может вернуть разное время.
В prod — ок, в тесте — ничего не сходится.
Airflow, dbt — сбиваются от timezone или минуты запуска.
Правильно:
→ один clock_timestamp() в CTE
→ или передавай дату явно
→ всегда указывай
▪️CAST()
А ты не в курсе, потому что SQL промолчал.
Правильно:
→
→ логировать NULL
→ комментировать формат явно
Кейс: отчёт по регистрациям. Столбец с '0000-00-00' → стал NULL → “а почему у нас 300 юзеров без даты?”
▪️ Как бы думал я
• Junior: “лишь бы работало”
• Senior: “чтобы было читаемо, проверяемо и поддерживаемо”
Вместо встроенных функций — оборачивай бизнес-логику в UDF:
→ видно, что происходит
→ можно покрыть тестами
→ можно писать changelog
▪️ Ну и куда, без тестов
• pgTAP или ручные проверки
• edge-cases: NULL, пустые строки, нули, текст
• COMMENT ON FUNCTION, changelog, CI с EXPLAIN ANALYZE
▪️ Кейс: баг, который "починили", а стало хуже
Ситуация: отчёт по выручке. Где-то в исходных данных
Аналитик пишет:
Отчёт выходит нормальным. Все рады. Только потом выясняется:
эти пользователи вообще не должны были попасть в выборку.
0 вместо NULL скрыл баг в логике JOIN, и теперь все графики — фальшивка.
Как нужно было сделать:
1. Заменил бы NULL — и задал вопрос: "почему здесь нет данных?"
2. Временная замена? Только с явным AS
3. Логика обработки — в отдельную UDF с бизнес-смыслом.
4. Плюс флажок
Не всякий NULL — враг. Иногда он — единственный сигнал, что что-то пошло не так.
💥 Как тебе ?
🔥 — узнал(а) что-то новое
🤝 — темы хорошо знакомы
💸 Поддержать канал
👉 Senior Data Analyst | #sql
Базовые SQL-функции кажутся удобными.
Но если применять их бездумно — это путь к тихим багам, кривым отчётам и вечному “а почему здесь 0?”
▪️Где сыпется чаще всего:
COALESCE / IFNULL
Выглядит мило:
COALESCE(value, 0)
Но скрывает NULL и мешает понять, откуда дыра.
В результате ты "лечишь" витрину, которая уже умерла.
Правильно:
→ Комментируй замену:
AS logic_zero_substitute
→ Лучше —
is_data_missing
как отдельная колонкаКейс: retention по оплатам. Пользователь не платил → NULL → подставили 0 → метрика выросла. Радоваться рано.
▪️ ROUND(..., 2)
Если ты округляешь до суммы — всё норм.
Если до — всё грустно.
Раннее округление:
→ копится погрешность
→ сумма строк != total
→ отчёт бьётся по швам
Правильно:
→ округляем после агрегации
→ лучше в обёртке типа
adjust_for_report(val)
▪️NOW() / CURRENT_TIMESTAMP
Самая коварная “удобная” функция.
Каждый вызов может вернуть разное время.
В prod — ок, в тесте — ничего не сходится.
Airflow, dbt — сбиваются от timezone или минуты запуска.
Правильно:
→ один clock_timestamp() в CTE
→ или передавай дату явно
→ всегда указывай
UTC
▪️CAST()
CAST('' AS INT)
— NULLCAST('2024-12-40' AS DATE)
— NULLА ты не в курсе, потому что SQL промолчал.
Правильно:
→
TRY_CAST
, если есть→ логировать NULL
→ комментировать формат явно
Кейс: отчёт по регистрациям. Столбец с '0000-00-00' → стал NULL → “а почему у нас 300 юзеров без даты?”
▪️ Как бы думал я
• Junior: “лишь бы работало”
• Senior: “чтобы было читаемо, проверяемо и поддерживаемо”
Вместо встроенных функций — оборачивай бизнес-логику в UDF:
adjusted_spend(spend, rate)
→ видно, что происходит
→ можно покрыть тестами
→ можно писать changelog
▪️ Ну и куда, без тестов
• pgTAP или ручные проверки
• edge-cases: NULL, пустые строки, нули, текст
• COMMENT ON FUNCTION, changelog, CI с EXPLAIN ANALYZE
▪️ Кейс: баг, который "починили", а стало хуже
Ситуация: отчёт по выручке. Где-то в исходных данных
revenue = NULL
, потому что JOIN
не сработал.Аналитик пишет:
SELECT COALESCE(revenue, 0) AS revenue_cleaned
Отчёт выходит нормальным. Все рады. Только потом выясняется:
эти пользователи вообще не должны были попасть в выборку.
0 вместо NULL скрыл баг в логике JOIN, и теперь все графики — фальшивка.
Как нужно было сделать:
1. Заменил бы NULL — и задал вопрос: "почему здесь нет данных?"
2. Временная замена? Только с явным AS
zero_if_missing
и комментарием.3. Логика обработки — в отдельную UDF с бизнес-смыслом.
4. Плюс флажок
is_data_missing
, чтобы не потерять контекст.Не всякий NULL — враг. Иногда он — единственный сигнал, что что-то пошло не так.
💥 Как тебе ?
🔥 — узнал(а) что-то новое
🤝 — темы хорошо знакомы
💸 Поддержать канал
👉 Senior Data Analyst | #sql
🔥7👍4❤2
🧠 Как ускорять SQL-запросы?
Если ты всё ещё веришь в “индекс спасёт всегда” — у меня для тебя EXPLAIN ANALYZE.
В посте:
– читаем план запроса, не глядя в потолок
– разбираем cost, actual rows, loops
– находим, где JOIN душит, а GROUP BY тормозит
– учимся ускорять без пересоздания БД
EXPLAIN — это рентген твоего SQL.
Хочешь писать запросы, которые не тормозят? Погнали 👇
💸 Поддержать канал
👉 Senior Data Analyst | #sql
Если ты всё ещё веришь в “индекс спасёт всегда” — у меня для тебя EXPLAIN ANALYZE.
В посте:
– читаем план запроса, не глядя в потолок
– разбираем cost, actual rows, loops
– находим, где JOIN душит, а GROUP BY тормозит
– учимся ускорять без пересоздания БД
EXPLAIN — это рентген твоего SQL.
Хочешь писать запросы, которые не тормозят? Погнали 👇
💸 Поддержать канал
👉 Senior Data Analyst | #sql
🔥3👏3
🛠 Как читать EXPLAIN и ускорять SQL-запросы
Часть 2: практичный гайд, без теории ради теории
▫️Что делает EXPLAIN?
Показывает, как именно PostgreSQL будет исполнять запрос:
что читает, в каком порядке, как соединяет таблицы, сколько строк ждёт и сколько реально вернул.
показывает фактическое выполнение:
• строки, прошедшие через каждый шаг
• время выполнения
• количество повторов (loops)
• сколько реально читали с диска
▫️Как читать план:
Тип операции:
Читай снизу вверх: от таблиц к джойнам и агрегациям
Rows / Loops / Time
•
•
• если
Total cost
Не время, а "оценка тяжести" запроса.
Используй для сравнения разных планов.
BUFFERS
Добавь
•
•
• если много
▫️Как реально ускорять запросы ?
1. Индексы по WHERE, JOIN, GROUP BY
Порядок колонок важен. Не ставь индексы "на всякий".
2. Делим фильтрацию и агрегацию
Вместо:
Так:
3. Следи за LEFT JOIN
Фильтр по правой таблице? Лучше вынеси в подзапрос.
PostgreSQL может выбрать ужасный план.
•
•
•
•
▫️Типичные ошибки:
• Seq Scan по большой таблице — нет фильтра или индекса
• JOIN по неиндексированному полю → Hash Join на всё
• Группировка по "широкой" колонке → сортировка в памяти
• Подзапрос без LIMIT — тянет всё
• ORDER BY без индекса — внешняя сортировка, тормоза
▫️Как нужно оптимизировать
•
• Сформулировать гипотезу (индекс, переписать логику)
• Внести минимальные изменения
• Проверить новый план
• Повторять, пока не будет норм
💥 Как тебе ?
🔥 — узнал(а) что-то новое
🤝 — темы хорошо знакомы
💸 Поддержать канал
👉 Senior Data Analyst | #sql
Часть 2: практичный гайд, без теории ради теории
▫️Что делает EXPLAIN?
Показывает, как именно PostgreSQL будет исполнять запрос:
что читает, в каком порядке, как соединяет таблицы, сколько строк ждёт и сколько реально вернул.
EXPLAIN ANALYZE
— то, что используем в проде:показывает фактическое выполнение:
• строки, прошедшие через каждый шаг
• время выполнения
• количество повторов (loops)
• сколько реально читали с диска
▫️Как читать план:
Тип операции:
Seq Scan
— чтение всей таблицы. Норм на маленьких, боль на большихIndex Scan
— идеал при точном фильтреBitmap Index Scan
— хорош при нескольких фильтрахNested Loop
— хорошо при малом внешнем набореHash Join
— рулит при JOIN ON a = b и достаточно памятиSort
— потенциально опасен, если не влезает в памятьЧитай снизу вверх: от таблиц к джойнам и агрегациям
Rows / Loops / Time
actual time=0.05..0.09 rows=1 loops=1
•
rows
— сколько вернул•
loops
— сколько раз вызвали• если
expected ≠ actual
— планировщик ошибается → неоптимальный планTotal cost
Не время, а "оценка тяжести" запроса.
Используй для сравнения разных планов.
BUFFERS
Добавь
EXPLAIN (ANALYZE, BUFFERS)
•
read
→ реально лезли на диск•
shared hit
→ всё в кеше• если много
read
→ нет индекса, не влезло в память▫️Как реально ускорять запросы ?
1. Индексы по WHERE, JOIN, GROUP BY
CREATE INDEX idx_orders_user_date ON orders(user_id, order_date);
Порядок колонок важен. Не ставь индексы "на всякий".
2. Делим фильтрацию и агрегацию
Вместо:
SELECT user_id, COUNT(*) FROM orders WHERE ... GROUP BY user_id;
Так:
WITH filtered AS (
SELECT user_id FROM orders WHERE ...
)
SELECT user_id, COUNT(*) FROM filtered GROUP BY user_id;
3. Следи за LEFT JOIN
Фильтр по правой таблице? Лучше вынеси в подзапрос.
PostgreSQL может выбрать ужасный план.
•
pg_stat_statements
— какие запросы дорогие•
pg_stat_user_indexes
— какие индексы реально нужны•
auto_explain
— лог запросов с плохими планами•
track_io_timing
— кто грузит диск▫️Типичные ошибки:
• Seq Scan по большой таблице — нет фильтра или индекса
• JOIN по неиндексированному полю → Hash Join на всё
• Группировка по "широкой" колонке → сортировка в памяти
• Подзапрос без LIMIT — тянет всё
• ORDER BY без индекса — внешняя сортировка, тормоза
▫️Как нужно оптимизировать
•
EXPLAIN ANALYZE
— найти узкое место• Сформулировать гипотезу (индекс, переписать логику)
• Внести минимальные изменения
• Проверить новый план
• Повторять, пока не будет норм
Важно не просто “сделать быстрее”, а понимать — почему стало быстрее.
💥 Как тебе ?
🔥 — узнал(а) что-то новое
🤝 — темы хорошо знакомы
💸 Поддержать канал
👉 Senior Data Analyst | #sql
🔥6🤝3
✅ Так можно визуализировать разницу между группами — понятно, гибко и без BI.
Bar-график с доверительными интервалами — для простого сравнения средних.
Boxplot со swarm — чтобы увидеть выбросы и структуру распределения.
KDE — когда хочется заглянуть в форму данных: есть ли смещение, мода, хвосты.
Чистый код, никаких кликов, полная автоматизация. Подходит, если ты работаешь в Jupyter, хочешь встроить график в пайплайн или просто устал от BI-интерфейсов.
💸 Поддержать канал
👉 Senior Data Analyst | #Python
Bar-график с доверительными интервалами — для простого сравнения средних.
Boxplot со swarm — чтобы увидеть выбросы и структуру распределения.
KDE — когда хочется заглянуть в форму данных: есть ли смещение, мода, хвосты.
Чистый код, никаких кликов, полная автоматизация. Подходит, если ты работаешь в Jupyter, хочешь встроить график в пайплайн или просто устал от BI-интерфейсов.
💸 Поддержать канал
👉 Senior Data Analyst | #Python
🔥5👌2
🧠 Бывает, нужно не просто показать «где среднее больше», а понять —
есть ли реальная разница между группами,
и насколько она вообще значимая.
Хочется сделать это:
— быстро
— без BI
— и без ощущения, что ты в Excel 2007
Вот три способа, которые работают.
Особенно если ты работаешь в Python, сидишь в Jupyter или просто не хочешь лезть в дашборды ради одной простой мысли: «а группы-то разные?»
1️⃣ Bar-график с доверительными интервалами
Что показывает:
— среднее значение по каждой группе
— вертикальные полоски — это 95% доверительные интервалы
Зачем это нужно:
Если интервалы не пересекаются — можно аккуратно говорить,
что между группами есть статистическая разница.
Почему работает:
— среднее понятно всем, даже продуктам
— CI выглядит серьёзно и просто одновременно
— идеально, когда надо вставить в презентацию или закинуть в чат
Минусы:
— если распределение несимметричное, картина может быть кривой
— на маленьких выборках CI становятся как шпаги — длинные и бесполезные
— 95% — это не истина, просто привычка
Когда использовать:
Когда надо быстро объяснить:
«вот здесь значение выше, и это не просто флуктуация, а, кажется, что-то реальное»
2️⃣ Boxplot + swarmplot
Классика с лицом. Видно всё, что происходит внутри.
Что показывает:
— медиану
— интерквартильный размах
— выбросы (boxplot)
— и каждую точку отдельно (swarm)
Зачем:
Такой график сразу показывает:
— где плотность
— где шум
— и где твоя метрика ведёт себя странно
Почему хорошо:
— boxplot привычен
— swarm даёт ощущение, что ты «видишь сырые данные»
— в паре они дают и форму, и структуру, и реальность
Минусы:
— на больших объёмах swarm превращается в хаос
— выбросы могут отвлекать, а могут быть важны
— визуально может быть перегружено, если неаккуратно оформить
Когда использовать:
Перед t-тестом, Mann-Whitney, или когда просто хочется понять:
✔️ всё ли ок внутри группы
✔️ не разваливается ли она на две разные
✔️ есть ли странности, которые лучше бы не игнорировать
3️⃣ KDE (плотности распределения)
Что показывает:
— гладкие кривые плотности по каждой группе
— можно сравнивать форму, центр, хвосты, моды
Зачем:
— идеально видно, насколько сильно и как именно отличаются группы
— симметрия, мультимодальность, сдвиги — всё читается с графика
Почему лучше, чем гистограмма:
— никакого биннинга
— линии — это не только красиво, но и удобно для сравнения
— если сделать аккуратно — график говорит сам за себя
Минусы:
— bandwidth (параметр сглаживания) решает всё: можно сгладить всё важное или показать только шум
— не работает с категориальными данными
— нормализация (common_norm) влияет на восприятие, с этим надо быть аккуратным
Когда использовать:
Для сравнения распределений:
✔️ до/после изменений в продукте
✔️поведение по сегментам
✔️время отклика, длина сессии, стоимость заказа
▪️Зачем вообще визуализировать в коде, а не в BI?
— полная свобода: цвета, подписи, стиль, порядок
— удобно встроить в пайплайн, ноутбук, автотест или даже Airflow
— не надо ждать, пока BI перерисует график
— можно расширять под задачу: добавить тесты, аннотации, пояснения, бренд
И главное:
ты не ограничен интерфейсом — только своей логикой и фантазией
▪️Итог
BI — это красиво. Но часто громоздко.
Если тебе нужен контроль, гибкость и повторяемость, визуализация через код — твой лучший друг.
Проверяешь гипотезу.
Объясняешь данные.
Делаешь так, чтобы все поняли — вот здесь действительно что-то изменилось.
💸 Поддержать канал
👉 Senior Data Analyst | #Python
есть ли реальная разница между группами,
и насколько она вообще значимая.
Хочется сделать это:
— быстро
— без BI
— и без ощущения, что ты в Excel 2007
Вот три способа, которые работают.
Особенно если ты работаешь в Python, сидишь в Jupyter или просто не хочешь лезть в дашборды ради одной простой мысли: «а группы-то разные?»
1️⃣ Bar-график с доверительными интервалами
Что показывает:
— среднее значение по каждой группе
— вертикальные полоски — это 95% доверительные интервалы
Зачем это нужно:
Если интервалы не пересекаются — можно аккуратно говорить,
что между группами есть статистическая разница.
Почему работает:
— среднее понятно всем, даже продуктам
— CI выглядит серьёзно и просто одновременно
— идеально, когда надо вставить в презентацию или закинуть в чат
Минусы:
— если распределение несимметричное, картина может быть кривой
— на маленьких выборках CI становятся как шпаги — длинные и бесполезные
— 95% — это не истина, просто привычка
Когда использовать:
Когда надо быстро объяснить:
«вот здесь значение выше, и это не просто флуктуация, а, кажется, что-то реальное»
2️⃣ Boxplot + swarmplot
Классика с лицом. Видно всё, что происходит внутри.
Что показывает:
— медиану
— интерквартильный размах
— выбросы (boxplot)
— и каждую точку отдельно (swarm)
Зачем:
Такой график сразу показывает:
— где плотность
— где шум
— и где твоя метрика ведёт себя странно
Почему хорошо:
— boxplot привычен
— swarm даёт ощущение, что ты «видишь сырые данные»
— в паре они дают и форму, и структуру, и реальность
Минусы:
— на больших объёмах swarm превращается в хаос
— выбросы могут отвлекать, а могут быть важны
— визуально может быть перегружено, если неаккуратно оформить
Когда использовать:
Перед t-тестом, Mann-Whitney, или когда просто хочется понять:
✔️ всё ли ок внутри группы
✔️ не разваливается ли она на две разные
✔️ есть ли странности, которые лучше бы не игнорировать
3️⃣ KDE (плотности распределения)
Что показывает:
— гладкие кривые плотности по каждой группе
— можно сравнивать форму, центр, хвосты, моды
Зачем:
— идеально видно, насколько сильно и как именно отличаются группы
— симметрия, мультимодальность, сдвиги — всё читается с графика
Почему лучше, чем гистограмма:
— никакого биннинга
— линии — это не только красиво, но и удобно для сравнения
— если сделать аккуратно — график говорит сам за себя
Минусы:
— bandwidth (параметр сглаживания) решает всё: можно сгладить всё важное или показать только шум
— не работает с категориальными данными
— нормализация (common_norm) влияет на восприятие, с этим надо быть аккуратным
Когда использовать:
Для сравнения распределений:
✔️ до/после изменений в продукте
✔️поведение по сегментам
✔️время отклика, длина сессии, стоимость заказа
▪️Зачем вообще визуализировать в коде, а не в BI?
— полная свобода: цвета, подписи, стиль, порядок
— удобно встроить в пайплайн, ноутбук, автотест или даже Airflow
— не надо ждать, пока BI перерисует график
— можно расширять под задачу: добавить тесты, аннотации, пояснения, бренд
И главное:
ты не ограничен интерфейсом — только своей логикой и фантазией
▪️Итог
BI — это красиво. Но часто громоздко.
Если тебе нужен контроль, гибкость и повторяемость, визуализация через код — твой лучший друг.
Проверяешь гипотезу.
Объясняешь данные.
Делаешь так, чтобы все поняли — вот здесь действительно что-то изменилось.
💸 Поддержать канал
👉 Senior Data Analyst | #Python
🔥7❤1
🧠 Python для аналитиков: циклы без лишнего кода
for, while и идиоматичный Python — вживую, по делу
Это вторая серия модуля 3, и она про то, как писать циклы так, чтобы не стыдно было.
генераторы,
Задачи:
— фильтрация CSV,
— отчёт по продажам,
— имитация банкомата (с ошибками, как в жизни)
После этой серии ты забудешь, как выглядят "петли ада" и начнёшь писать коротко, понятно и без лишних if'ов.
💸 Поддержать канал, сказать «ну кайф» и скачать серию можно тут
❤️ И да, поставь реакцию за старание
👉 Senior Data Analyst #python #модуль_03 #серия_02
for, while и идиоматичный Python — вживую, по делу
Это вторая серия модуля 3, и она про то, как писать циклы так, чтобы не стыдно было.
for
— от списков до словарей, с zip() и enumerate()while
— когда не знаешь, сколько раз, но точно надогенераторы,
any
, all
, sorted
— чтоб писать красивоЗадачи:
— фильтрация CSV,
— отчёт по продажам,
— имитация банкомата (с ошибками, как в жизни)
После этой серии ты забудешь, как выглядят "петли ада" и начнёшь писать коротко, понятно и без лишних if'ов.
💸 Поддержать канал, сказать «ну кайф» и скачать серию можно тут
❤️ И да, поставь реакцию за старание
👉 Senior Data Analyst #python #модуль_03 #серия_02
5❤7
⚠️ Три SQL-ловушки даже для опытных
1. LEFT JOIN, который стал INNER
Думал, берёшь всех пользователей, даже без заказов? А фильтр по правой таблице в WHERE выкинул NULL-ы — и всё, отчёт «похудел».
Если нужен INNER — пиши его явно.
2. DISTINCT как пластырь
Дубликаты? DISTINCT скроет их, но не исправит причину — кривые ключи или «многие-ко-многим». На больших объёмах ещё и тормозит.
Найди и почини источник дублей.
3. Фильтры, что убивают индексы
Убирай функции из фильтров или храни отдельные поля для поиска.
💸 Поддержать канал
👉 Senior Data Analyst | #sql
1. LEFT JOIN, который стал INNER
Думал, берёшь всех пользователей, даже без заказов? А фильтр по правой таблице в WHERE выкинул NULL-ы — и всё, отчёт «похудел».
Если нужен INNER — пиши его явно.
2. DISTINCT как пластырь
Дубликаты? DISTINCT скроет их, но не исправит причину — кривые ключи или «многие-ко-многим». На больших объёмах ещё и тормозит.
Найди и почини источник дублей.
3. Фильтры, что убивают индексы
DATE(col) = '2025-01-01'
или LIKE '%abc'
лишают индекс шанса работать. На тестах всё быстро, а на проде — скан миллионов строк.Убирай функции из фильтров или храни отдельные поля для поиска.
💸 Поддержать канал
👉 Senior Data Analyst | #sql
👍5🤔1
⚠️ SQL и три ловушки, которые должен видеть Senior
▪️Почему важно копать глубже?
На ревью SQL‑кода легко проверить только синтаксис, названия колонок и то, что результат «похож» на ожидаемый. Но на уровне senior задачи другие:
• Проверить бизнес‑логику — запрос должен возвращать ровно то, что задумано.
• Обеспечить инженерное качество — избежать скрытых ошибок и деградации.
• Гарантировать читаемость — код должны понимать и поддерживать другие.
SQL‑запросы редко живут сами по себе: они попадают в витрины, отчёты, фичи для моделей. Любой компромисс «на авось» со временем размножается и становится проблемой.
▪️Ловушка 1: LEFT JOIN, который «тихо» стал INNER
Симптом: используется LEFT JOIN, но в WHERE добавлен фильтр по колонке правой таблицы. Это меняет смысл соединения — строки без совпадений исчезают.
Опасность:
• Тихое искажение логики: запрос, который должен возвращать всех клиентов, внезапно отбрасывает часть.
• Проблема заметна не сразу — в отчётах «пропадают» данные.
• Разные ожидания у участников команды.
Как обнаружить: при ревью искать конструкции
Что делать:
• Переносить условие в ON, если оно относится к соединению.
• Если соединение должно быть INNER, писать явно.
• Для сложных фильтров предварительно отфильтровать данные правой таблицы.
▪️Ловушка 2: DISTINCT как «пластырь» на неправильный JOIN
Симптом: DISTINCT или COUNT(DISTINCT) в запросе для устранения дублей.
Опасность:
• Скрывает причину дубликатов — чаще всего это неправильные ключи или соединение «многие‑ко‑многим».
• DISTINCT замедляет выполнение на больших объёмах.
• Может приводить к потере нужных данных.
Как обнаружить: задать вопрос автору: «Почему тут DISTINCT?». Проверить кардинальность соединений и условия.
Что делать:
• Явно выбирать одну нужную строку с помощью оконных функций или подзапросов.
• Агрегировать данные до соединения.
• Использовать DISTINCT только там, где это действительно часть бизнес‑логики.
▪️Ловушка 3: Несаржабельные фильтры, которые убивают индексы
Симптом: в WHERE применяются функции к колонкам, используется LIKE с ведущим %, или происходит приведение типов на лету.
Опасность:
• Индекс по колонке не используется.
• На тестах запрос быстрый, а на бою уходит в полное сканирование.
• Проблема проявляется при росте объёмов данных.
Как обнаружить: искать функции в фильтрах
Что делать:
• Переписывать фильтры, чтобы индекс мог быть использован (работать с диапазонами дат, хранить предрасчитанные значения).
• Денормализовать часто фильтруемые значения (например, выделить домен email в отдельную колонку).
• Использовать специализированные индексы (GIN, BRIN, trigram).
▪️ Чек‑лист Senior на ревью SQL
• LEFT JOIN + WHERE по правой таблице? → проверить логику.
• DISTINCT / COUNT(DISTINCT)? → действительно ли нужен.
• Кардинальность соединений ясна и контролируема.
• Функции на колонках в WHERE? → переписать под индексы.
• LIKE с ведущим %? → оптимизировать.
• Индексы соответствуют условиям фильтрации.
• План запроса не ушёл в Seq Scan без причины.
💥 Как тебе ?
🔥 — узнал(а) что-то новое
🤝 — темы хорошо знакомы
💸 Поддержать канал
👉 Senior Data Analyst | #sql
Разбор трёх частых анти‑паттернов в SQL, которые встречаются даже у опытных разработчиков. Почему они опасны, как их заметить на ревью и что с ними делать.
▪️Почему важно копать глубже?
На ревью SQL‑кода легко проверить только синтаксис, названия колонок и то, что результат «похож» на ожидаемый. Но на уровне senior задачи другие:
• Проверить бизнес‑логику — запрос должен возвращать ровно то, что задумано.
• Обеспечить инженерное качество — избежать скрытых ошибок и деградации.
• Гарантировать читаемость — код должны понимать и поддерживать другие.
SQL‑запросы редко живут сами по себе: они попадают в витрины, отчёты, фичи для моделей. Любой компромисс «на авось» со временем размножается и становится проблемой.
▪️Ловушка 1: LEFT JOIN, который «тихо» стал INNER
Симптом: используется LEFT JOIN, но в WHERE добавлен фильтр по колонке правой таблицы. Это меняет смысл соединения — строки без совпадений исчезают.
Опасность:
• Тихое искажение логики: запрос, который должен возвращать всех клиентов, внезапно отбрасывает часть.
• Проблема заметна не сразу — в отчётах «пропадают» данные.
• Разные ожидания у участников команды.
Как обнаружить: при ревью искать конструкции
LEFT JOIN
+ фильтр по колонкам правой таблицы в WHERE.Что делать:
• Переносить условие в ON, если оно относится к соединению.
• Если соединение должно быть INNER, писать явно.
• Для сложных фильтров предварительно отфильтровать данные правой таблицы.
▪️Ловушка 2: DISTINCT как «пластырь» на неправильный JOIN
Симптом: DISTINCT или COUNT(DISTINCT) в запросе для устранения дублей.
Опасность:
• Скрывает причину дубликатов — чаще всего это неправильные ключи или соединение «многие‑ко‑многим».
• DISTINCT замедляет выполнение на больших объёмах.
• Может приводить к потере нужных данных.
Как обнаружить: задать вопрос автору: «Почему тут DISTINCT?». Проверить кардинальность соединений и условия.
Что делать:
• Явно выбирать одну нужную строку с помощью оконных функций или подзапросов.
• Агрегировать данные до соединения.
• Использовать DISTINCT только там, где это действительно часть бизнес‑логики.
▪️Ловушка 3: Несаржабельные фильтры, которые убивают индексы
Симптом: в WHERE применяются функции к колонкам, используется LIKE с ведущим %, или происходит приведение типов на лету.
Опасность:
• Индекс по колонке не используется.
• На тестах запрос быстрый, а на бою уходит в полное сканирование.
• Проблема проявляется при росте объёмов данных.
Как обнаружить: искать функции в фильтрах
(DATE(col)
, LOWER(col))
, проверять шаблоны поиска, смотреть план запроса.Что делать:
• Переписывать фильтры, чтобы индекс мог быть использован (работать с диапазонами дат, хранить предрасчитанные значения).
• Денормализовать часто фильтруемые значения (например, выделить домен email в отдельную колонку).
• Использовать специализированные индексы (GIN, BRIN, trigram).
▪️ Чек‑лист Senior на ревью SQL
• LEFT JOIN + WHERE по правой таблице? → проверить логику.
• DISTINCT / COUNT(DISTINCT)? → действительно ли нужен.
• Кардинальность соединений ясна и контролируема.
• Функции на колонках в WHERE? → переписать под индексы.
• LIKE с ведущим %? → оптимизировать.
• Индексы соответствуют условиям фильтрации.
• План запроса не ушёл в Seq Scan без причины.
💥 Как тебе ?
🔥 — узнал(а) что-то новое
🤝 — темы хорошо знакомы
💸 Поддержать канал
👉 Senior Data Analyst | #sql
🔥9🤝1
💬 Почему сеньор не отвечает на задачу сразу?
Потому что сначала уточняет, что именно нужно.
На первый взгляд кажется, что он просто тянет время.
А на деле — экономит и своё, и твоё время.
Размытый запрос = риск запутаться в терминах, насыпать лишних данных и угробить пару часов впустую.
Обычно вопросы такие:
• Зачем нужны эти данные?
• Какие именно поля важны?
• В каком виде будет удобнее получить результат?
• Когда это критично?
💸 Поддержать канал
👉 Senior Data Analyst | #soft #product
Потому что сначала уточняет, что именно нужно.
На первый взгляд кажется, что он просто тянет время.
А на деле — экономит и своё, и твоё время.
Размытый запрос = риск запутаться в терминах, насыпать лишних данных и угробить пару часов впустую.
Обычно вопросы такие:
• Зачем нужны эти данные?
• Какие именно поля важны?
• В каком виде будет удобнее получить результат?
• Когда это критично?
2 минуты на уточнения = часы, сэкономленные на переделках.
Это не бюрократия, а страховка от бессмысленной работы.
💸 Поддержать канал
👉 Senior Data Analyst | #soft #product
🔥4🤝1
🐢 Почему сеньор не отвечает сразу: искусство уточнять задачу
Сеньор-аналитик — это не «гуру SQL, который помнит все джоины наизусть».
Это человек, который сначала задаёт вопросы, а уже потом открывает SQL.
1. Миф о «быстром ответе»
Во многих командах ценится оперативность:
Но скорость ≠ ценность.
Можно потратить 10 минут на запрос, который:
• никому не нужен в таком виде;
• считает не те поля;
• не решает исходную задачу.
В итоге — переделки, двойная работа и раздражение.
Поэтому начинаем не с кода, а с уточнений.
2. Зачем вообще уточнять
Часто заказчик описывает решение, а не проблему.
Пример:
А на самом деле может быть, что:
• нужен расчёт churn rate;
• интересуют только активные клиенты;
• речь про конкретный сегмент;
• цель — слайд для совета директоров.
А значит фильтруем всё через призму:
• Что хотим понять?
• Почему нужны именно эти данные?
• Как это повлияет на решение?
3. Как это выглядит на разных уровнях
→ Джун — берёт задачу как есть, максимум уточняет формат и дедлайн.
→ Мидл — спрашивает про поля, источники, фильтры; понимает ценность данных, но не всегда видит весь контекст.
→ Сеньор — выясняет цель, согласует метрики, проверяет, есть ли готовое решение, и предлагает самый удобный способ.
4. Мини-фреймворк SCOPE
S — Scope: объём, сегменты, периоды, фильтры.
C — Context: какую проблему решаем и кто примет решение.
O — Output: формат — выгрузка, дашборд, автоотчёт.
P — Priority: как это по срочности относительно других задач.
E — Exceptions: что исключаем, какие риски.
5. Кейсы
📍 Из выгрузки в дашборд
Запрос: «Скинь продажи по регионам за прошлый месяц».
После уточнений: нужен еженедельный мониторинг + доступ для 3 отделов → сделали дашборд в Tableau, экономим 15 часов в месяц.
📍 Уточнение спасло от спама мёртвым клиентам
Запрос: «Клиенты без покупок 6+ месяцев».
После уточнений: добавили фильтр “открывал рассылки за 3 месяца” → в кампанию попали только живые пользователи.
6. Когда не уточняешь
• Размытые термины («активный клиент» = разные цифры у разных команд).
• Неверный период (30 дней ≠ календарный месяц).
• Игнор контекста (миллиард строк вместо нужной агрегации).
• Срыв приоритетов (взялся за «срочную» задачу и завалил реально важную).
7. Как прокачать навык
• Не бойся казаться медленным — страшнее сделать бесполезно.
• Используй чек-лист вопросов.
• Переформулируй задачу и жди подтверждения.
• Веди базу определений метрик.
8. Пример живого диалога
— Скинь продажи по Москве за прошлый квартал.
— А для чего? Отчёт или анализ трендов?
— Для презентации руководству.
— Сравнение с прошлым кварталом или и с прошлым годом?
— И с годом тоже.
— Таблица или график?
— График.
Запрос → Excel
Результат → график с трендом и комментариями прямо в презентации.
Попробуй: на каждый новый запрос — задай хотя бы 3 уточняющих вопроса.
Посмотри, сколько переделок удастся избежать.
💥 Как тебе ?
🔥 — узнал(а) что-то новое
🤝 — темы хорошо знакомы
💸 Поддержать канал
👉 Senior Data Analyst | #soft #product
Сеньор-аналитик — это не «гуру SQL, который помнит все джоины наизусть».
Это человек, который сначала задаёт вопросы, а уже потом открывает SQL.
1. Миф о «быстром ответе»
Во многих командах ценится оперативность:
— «Попросили выгрузку — сделал за 10 минут».
Но скорость ≠ ценность.
Можно потратить 10 минут на запрос, который:
• никому не нужен в таком виде;
• считает не те поля;
• не решает исходную задачу.
В итоге — переделки, двойная работа и раздражение.
Поэтому начинаем не с кода, а с уточнений.
2. Зачем вообще уточнять
Часто заказчик описывает решение, а не проблему.
Пример:
«Скинь выгрузку по клиентам за прошлый месяц».
А на самом деле может быть, что:
• нужен расчёт churn rate;
• интересуют только активные клиенты;
• речь про конкретный сегмент;
• цель — слайд для совета директоров.
А значит фильтруем всё через призму:
• Что хотим понять?
• Почему нужны именно эти данные?
• Как это повлияет на решение?
3. Как это выглядит на разных уровнях
→ Джун — берёт задачу как есть, максимум уточняет формат и дедлайн.
→ Мидл — спрашивает про поля, источники, фильтры; понимает ценность данных, но не всегда видит весь контекст.
→ Сеньор — выясняет цель, согласует метрики, проверяет, есть ли готовое решение, и предлагает самый удобный способ.
4. Мини-фреймворк SCOPE
S — Scope: объём, сегменты, периоды, фильтры.
C — Context: какую проблему решаем и кто примет решение.
O — Output: формат — выгрузка, дашборд, автоотчёт.
P — Priority: как это по срочности относительно других задач.
E — Exceptions: что исключаем, какие риски.
5. Кейсы
📍 Из выгрузки в дашборд
Запрос: «Скинь продажи по регионам за прошлый месяц».
После уточнений: нужен еженедельный мониторинг + доступ для 3 отделов → сделали дашборд в Tableau, экономим 15 часов в месяц.
📍 Уточнение спасло от спама мёртвым клиентам
Запрос: «Клиенты без покупок 6+ месяцев».
После уточнений: добавили фильтр “открывал рассылки за 3 месяца” → в кампанию попали только живые пользователи.
6. Когда не уточняешь
• Размытые термины («активный клиент» = разные цифры у разных команд).
• Неверный период (30 дней ≠ календарный месяц).
• Игнор контекста (миллиард строк вместо нужной агрегации).
• Срыв приоритетов (взялся за «срочную» задачу и завалил реально важную).
7. Как прокачать навык
• Не бойся казаться медленным — страшнее сделать бесполезно.
• Используй чек-лист вопросов.
• Переформулируй задачу и жди подтверждения.
• Веди базу определений метрик.
8. Пример живого диалога
— Скинь продажи по Москве за прошлый квартал.
— А для чего? Отчёт или анализ трендов?
— Для презентации руководству.
— Сравнение с прошлым кварталом или и с прошлым годом?
— И с годом тоже.
— Таблица или график?
— График.
Запрос → Excel
Результат → график с трендом и комментариями прямо в презентации.
Попробуй: на каждый новый запрос — задай хотя бы 3 уточняющих вопроса.
Посмотри, сколько переделок удастся избежать.
💥 Как тебе ?
🔥 — узнал(а) что-то новое
🤝 — темы хорошо знакомы
💸 Поддержать канал
👉 Senior Data Analyst | #soft #product
🔥14🤝1
📌 Теория: Теория вероятностей на пальцах — что должен знать каждый аналитик
Вся аналитика строится на вероятностях — даже если вы не замечаете этого.
Ниже — ключевые концепты с примерами на Python, которые пригодятся в SQL-запросах, A/B-тестах, продуктовых метриках и ML-моделях.
Краткие выводы:
P(A|B) — используется в пользовательских воронках и последовательном поведении
Байес — основа спам-фильтров, предиктивных моделей и рекомендаций
Сложение вероятностей — важно для уникальных пользователей и перекрывающихся действий
Binom/Norm/Poisson — фундамент A/B-тестов, ML и оценки редких событий
Совет
Изучайте вероятность через реальные задачи: A/B-тесты, email-рассылки, CTR, прогнозирование поведения. Тогда теория «зайдет» легко и навсегда.
Эта база позволяет перейти от описательной аналитики к вероятностной. Вернись к ней, когда будешь считать конверсии, воронки или доверительные интервалы.
💸 Поддержать канал
👉 Senior Data Analyst | #python
Вся аналитика строится на вероятностях — даже если вы не замечаете этого.
Ниже — ключевые концепты с примерами на Python, которые пригодятся в SQL-запросах, A/B-тестах, продуктовых метриках и ML-моделях.
Краткие выводы:
P(A|B) — используется в пользовательских воронках и последовательном поведении
Байес — основа спам-фильтров, предиктивных моделей и рекомендаций
Сложение вероятностей — важно для уникальных пользователей и перекрывающихся действий
Binom/Norm/Poisson — фундамент A/B-тестов, ML и оценки редких событий
Совет
Изучайте вероятность через реальные задачи: A/B-тесты, email-рассылки, CTR, прогнозирование поведения. Тогда теория «зайдет» легко и навсегда.
Эта база позволяет перейти от описательной аналитики к вероятностной. Вернись к ней, когда будешь считать конверсии, воронки или доверительные интервалы.
💸 Поддержать канал
👉 Senior Data Analyst | #python
1🤝9❤1🔥1
◾️ Нормализация vs Денормализация
Зачем вообще спорить про это?
Потому что в нормализации удобно жить данным, а в денормализации — людям.
Если совсем по-простому: нормализация спасает от бардака и дублей, денормализация — от длинных и медленных запросов. В нормализованном мире удобно жить данным, в денормализованном — людям. В зрелых системах нужны оба подхода — просто в разных слоях.
Нормализация
Денормализация
В реальности не «или-или».
👉 Senior Data Analyst | #sql #dwh
Зачем вообще спорить про это?
Потому что в нормализации удобно жить данным, а в денормализации — людям.
Если совсем по-простому: нормализация спасает от бардака и дублей, денормализация — от длинных и медленных запросов. В нормализованном мире удобно жить данным, в денормализованном — людям. В зрелых системах нужны оба подхода — просто в разных слоях.
Нормализация
Каждая таблица про одну сущность, связи через ключи.
• Целостность: «Москва» и «Moscow» не заведутся параллельно.
• Обновления — в одном месте.
– Аналитика требует JOIN’ов (иногда 5–6 подряд).
👉 Лучший выбор для OLTP (CRM, биллинг, ERP).
Денормализация
Широкие таблицы: всё нужное уже под рукой.
• Запросы проще и быстрее.
• Отлично для BI и ML.
– Дублирование, сложнее обновлять.
👉 Оптимальна для OLAP (DWH, витрины).
В реальности не «или-или».
Архитектура обычно такая:
• Staging → сырые данные,
• Core → нормализованное ядро,
• Marts → денормализованные витрины.
👉 Senior Data Analyst | #sql #dwh
👍6