Дмитрий Кузьмин. Инженерия данных
1K subscribers
51 photos
4 videos
46 links
Путь Data engineer от junior до lead.

Делюсь мыслями, рабочими кейсами, обучением. Блог для junior - middle DE.

🌐 Репозиторий: https://github.com/dim4eg91/DataEngineering/tree/main
📱 Мой профиль: @dim4eg91
📚 Сайт: https://kuzmin-dmitry.ru
Download Telegram
🗓 «31 ноября, потерянный пароль и возвращение к реальности»

Друзья, привет!
Ну что, как понять, что новогодний отдых удался, а вы готовы снова вливаться в рабочий ритм? У меня вчерашний день получился довольно… необычным :)

☀️ С утра я на полном серьезе не сразу понял, зачем и куда мне так срочно нужно. Ведь там, казалось, меня никто особо не ждет :)
А дальше была эпичная битва с паролем от рабочего компа, которая длилась минут 30. Итог — пароль сдался, а я стал чуточку счастливее.

⁉️ А еще в середине дня у меня случилась настоящая загадка. Почему потоковые данные за 31 ноября не хотят считаться? Спойлер: потому что такого дня нет. Но осознание этого факта пришло не сразу, а когда я уже порядком повеселился :)

🔄 В общем, считаю, что отдых удался. Успел расслабиться, перевести дух, хотя где-то внутри еще и хочется немного побездельничать. Но ничего, будем разгоняться и входить в рабочий процесс. Со своей стороны постараюсь регулярно подкидывать вам что-то полезное: кому-то — новое, кому-то — для освежения знаний. Это всегда полезно.

Если с вами вчера тоже приключилось что-то забавное — делитесь, будет интересно почитать! 😮
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥9👍3😁3🤓21
📀 Распределенные системы и с чего начать?

Вы когда-нибудь задумывались, как ваши фотки с отпуска попадают в облако и остаются там навечно, даже если вы забыли пароль от аккаунта? Это всё благодаря распределённым системам.

На первый взгляд кажется, что данные просто «улетели в облако», но на самом деле их обрабатывают сотни серверов, работающих вместе, как хорошо слаженный оркестр — только вместо музыки они гоняют петабайты данных.

⚒️ Для Data Engineer распределённые системы — это must have, потому что они строят решения, которые должны работать на больших объёмах данных. Нельзя просто взять один сервер и заставить его хранить терабайты — он рано или поздно сгорит, как старый утюг. Поэтому данные хранятся на множестве машин. Обработка тоже должна быть быстрой — миллионы записей одним SQL-запросом за пару секунд обычный сервер не осилит, здесь на помощь приходят Spark и Hadoop. И, конечно, отказоустойчивость: если один сервер упал, система должна продолжить работать, а не напоминать цирк с обвалившимися декорациями. Без распределённых систем ни крупные ETL-процессы, ни современные аналитические платформы попросту не взлетят.

💡Это крайне важная тема, которую обязательно нужно изучить после того, как ты освоил SQL. Знание SQL — это основа, но в реальной жизни запросы пишутся не для маленьких баз данных, а для огромных хранилищ, которые работают по принципам распределённых систем. Поэтому, если хотите расти, как инженер данных, без понимания этой темы — никуда.

🛤️ Для каких направлений это важно?

*️⃣Big Data — очевидно, что здесь без распределённых систем никуда. Хранилища вроде HDFS, системы потоковой обработки вроде Apache Kafka — всё это работает в кластере.

*️⃣Data Warehousing — распределённые хранилища вроде Amazon Redshift, Google BigQuery, Greenplum, Snowflake работают по принципу распределённой архитектуры. Когда ты пишешь SQL-запрос к такому хранилищу, за кулисами распределённые узлы решают, как этот запрос исполнить максимально эффективно.

*️⃣Machine Learning на больших данных — Когда данных так много, что одной машине их не переварить, модели обучают сразу на множестве серверов.

📚 С чего начать изучение?

#️⃣Понять основы — начните с теории: что такое распределённые системы, зачем они нужны, какие есть основные концепции — партиционирование, репликация данных и т.п.

#️⃣Попробовать руками — возьмите что-то простое, вроде установки кластера Hadoop или запуска Spark на нескольких узлах. Вы также можете поставить Spark на одном ноутбуке, можно симулировать кластер в Docker (расскажу в серии постов, как это легко сделать).

#️⃣Изучить инструменты:
Apache Kafka — основа потоковой обработки данных.
Apache Spark — распределённая обработка данных в кластере.
HDFS / S3 — распределённые файловые хранилища.

#️⃣Практиковаться — самый крутой способ научиться — это построить что-то своё. Например, возьми небольшой проект: собери данные о погоде из разных источников, сложи их в HDFS и проанализируй с помощью Spark.

⬇️Распределённые системы — это сердце любой современной дата-инженерной архитектуры. Они обеспечивают масштабируемость, скорость и надёжность. Даже если вы сейчас только начинаете путь в DE, понимание этих систем даст вам огромное преимущество.

Ведь кто строит мосты между миром данных и бизнесом? Data Engineers. А значит, без распределённых систем — никуда.

Это я так плавно вас подвожу к Hadoop и Spark 💪
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥114👍3👏2
#путь_de

Когда-то в 2021 году
мне довелось пройти курс по Hadoop от Сбера. Тогда я ещё не имел практического опыта ни с Python, ни с распределёнными системами, но внутренне чувствовал: это то, что приведёт меня к интересным проектам в Big Data!

По мере углубления понял, что теоретические знания — это одно, а реальная практика на работе — совсем другое. Когда я перешёл в новую команду и начал работать со Spark, то ощутил тот самый «буст» 🚀, которого так ждал. Теперь, спустя почти год реальной работы, я могу сказать: основы Spark осилить реально, но глубина может быть огромной.

⚙️ Два условных направления Data Engineering

Сегодня часто можно заметить два условных «лагеря» в мире Data Engineering:

1. Инженеры, которые работают на Spark
🔸 Сосредоточены на сборе данных из разных источников (файловые хранилища, потоки, базы).
🔸 Делают первичную или сложную обработку, трансформации, очищают данные, пишут ETL.
🔸 Готовят и загружают результат в DWH или другие системы.

2. Инженеры, которые «живут» в DWH (Greenplum или другое)
🔹 Отвечают за организацию и хранение данных в хранилище.
🔹 Нарезают данные по слоям, строят витрины и оптимизируют запросы.
🔹 Глубоко разбираются в архитектуре MPP/SQL, знают, как распределять и партиционировать таблицы.

⭐️ Почему я выделяю Spark и Greenplum?

🔸 Spark: это фреймворк для распределённых вычислений, который помогает обрабатывать большие объёмы данных. Он гибок и масштабируем, но требует чёткого понимания:
- Как устроена концепция DataFrame, Dataset и RDD.
- Как грамотно настраивать кластеры и оптимизировать job, чтобы не «утонуть» в ресурсах.
- Как работать с разными форматами данных (CSV, Parquet, JSON и т.д.).

🔹 Greenplum: это MPP-хранилище (Massively Parallel Processing), базирующееся на PostgreSQL. Отлично подходит для:
- Хранения больших массивов данных, когда нужно параллелить SQL-запросы.
- Аналитической обработки, построения сложных отчётов и витрин.
- Глубокой работы с SQL, оптимизацией запросов, грамотным распределением данных по сегментам.

Ключевые различия (коротко)

- Фокус: Spark ориентирован на распределённые вычисления и ETL, Greenplum — на хранение и аналитические SQL-запросы.
- Язык: у Spark — это чаще Python/Scala, а у Greenplum — SQL.
- Масштаб: Spark «растёт» под задачу динамично, Greenplum масштабируется за счёт параллельной архитектуры внутри базы.

Требования к знаниям и навыкам

Чтобы уверенно чувствовать себя в Spark, желательно:
- Понимать модель распределённых вычислений (как данные делятся и обрабатываются параллельно).
- Владеть Python/Scala (или Java) на уровне написания, отладки и оптимизации ETL.
- Уметь читать Spark UI, разбираться в метриках выполнения, настраивать драйверы и экзекьюторы.

Чтобы быть спецом в Greenplum (или другом DWH), будет круто:
- Глубоко понимать реляционные модели и SQL.
- Знать принципы MPP: распределение строк по сегментам, партиционирование, индексирование.
- Уметь настраивать и оптимизировать запросы, использовать Explain/Analyze.

Моя мотивация для вас

Если вы только в начале пути или чувствуете недостаток практики: не переживайте! Главное — это желание осваивать новые вещи и постепенно погружаться в детали. От ETL в Spark до построения витрин в Greenplum — всё это огромный мир Data Engineering, где каждый может найти то, что ему нравится, и стать в этом экспертом.

А совместное использование Spark и Greenplum открывает огромные возможности для компании и для вашего профессионального роста. Дерзайте, учитесь, экспериментируйте — пусть большие данные станут вашим катализатором карьерного успеха! 🚀
👍209🔥4🤝1
На днях я изрядно порвал шаблон
Я пишу на Spark SQL довольно давно. Но тут наткнулся на странность: мне нужно было получить конкретную выборку, я уже знал, что приблизительно должно получиться, но оно не получалось!

Где-то пол часа я вообще не мог понять, в чем дело, грешил на неверные JOIN'ы... А потом обнаружил самое банальное: кто-то (я) забыл про скобочки в WHERE-условии. Вот что я написал (внимание на WHERE блок):
SELECT * FROM table
WHERE 1=1
AND condition_1
AND condition_2 OR condition_3


То есть condition_3 проверялся независимо от condition_2, и я получал неверные результаты.

Что должно было быть:
SELECT * FROM table
WHERE 1=1
AND condition_1
AND (condition_2 OR condition_3)


Даже смешно как-то стало, когда я это понял 😅

Казалось бы, самая элементарная вещь, но с каждым может случиться: усталость, дедлайны, всякие отвлекающие факторы — и вот уже сидишь и сомневаешься везде и во всём.

Не забудьте поставить скобочки! Или лучше дважды проверьте, что они там, где нужно.

Товарищи мидлы и сеньоры, вы допускаете такие ошибки? 🤔

Поделитесь своими “топовыми” ошибками, которые вспоминаются с улыбкой. Ведь даже эксперты — обычные люди, которым свойственно ошибаться. Главное вовремя увидеть и исправить!
Please open Telegram to view this post
VIEW IN TELEGRAM
👍23😁84🔥3
#база_знаний

HDFS: архитектура, полезные команды и интересные вопросы.

Блиц материал по этой системе хранения. Если вы работаете не только с БД, но еще и с обработкой больших данных, HDFS остаётся актуальным, даже когда все вокруг говорят про Cloud 💭 .

Напоминаю о том:
Как работает HDFS и зачем он нужен
🔒 что такое репликация и отказоустойчивость
💬 а также привожу список интересных вопросов на подумать и изучить

Вероятно попозже сделаю разбор вопросов или какой-нибудь тест для вашей самопроверки. Если интересно, накидайте реакций 😏
Please open Telegram to view this post
VIEW IN TELEGRAM
👍19🔥4👌3
#путь_DE

✏️ Не так давно я писал про культуру разработки и ведения Git.

На работе я использовал Bitbucket для хранения кода релиза, сейчас его же для кода витрин и view, а дома для пет проектов и даже репозитория канала использую GitHub.

😮 Но тут на прошлом спринте я впервые столкнулся с моим первым настоящим code review от моего лида. Это выглядело довольно занимательно и интригующе. Я понимаю, что неоптимальный код должен быть отловлен еще до стадии push в репо. И когда я получил обратно свой код с 20 комментами, то словил двоякое ощущение:
1️⃣ у меня много ошибок, как я так много накосячил, это все увидели. То есть это не конечный результат в виде parquet или любого другого файла, а именно логика ETL с моим стилем кода.
2️⃣ ощущение интереса и роста, поскольку более опытные DE прямо указывает места, которые можно подтюнить здесь и сейчас.

На фикс моего pull request я потратил 2 дня, не без помощи коллеги. Но столкнуться и разобраться с этим было довольно здорово.

И тут пришел еще один инсайт: если у вас есть возможность на проекте, после разработки кидайте ваш код не в файлы локально, а в репо (командное или личное).

Это даст вам несколько преимуществ:

🔹 Вы будите получать опыт разработки и ведения проекта в git по всем канонам. То есть не затрагивая master часть приложения / БД / ETL. Разработка новой фичи / витрины / скрипта будет происходить у вас в новой ветке, а выкат этого в master будет безболезненным с code review.

🔹 Коллеги в любой момент смогут обратиться к вашему коду без вас. Удобно, поскольку совсем недавно я разработал логику для нескольких view, которые отдают данные для BI, и хранил скрипты локально. Конечно, можно посмотреть DDL в БД, но лучше иметь человеческий код под рукой.

🔹 на многих проектах нужен опыт с Git (или аналогами), а тут у вас уже будет набита рука на базовых и необходимых действиях с ним.

😠 Фатальный кейс - если кто-то дропнет (случайно) эти view, а код их создания локально, то тут можно понервничать (со мной и это было 👀)
Please open Telegram to view this post
VIEW IN TELEGRAM
👍121🔥1😁1🤝1
#база_знаний

💻 Оконные функции — звучит страшно, но на деле спасают жизнь.

Друзья, доброго понедельника! Сегодня хочу поделиться, зачем DE знать оконные функции, они же «окошки» или «оконки».

Оконные функции я начал активно изучать только тогда, как увидел их в рабочем коде коллег.
Раньше всегда почему-то откладывал — казалось, что это что-то из высшей математики. Но нет.

👉 На деле — это просто способ посмотреть на соседние строки или преобразовать датафрейм более удобным способом.

Кейс 1: найти последнюю дату по каждому клиенту

Задача: у каждого клиента нужно выбрать только его самый «свежий» договор или заказ.

SELECT *
FROM (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY client_id ORDER BY contract_date DESC) AS rn
FROM contracts
) t
WHERE rn = 1;


Обычный GROUP BY тут не поможет — он не подтянет все остальные поля.

А ROW_NUMBER() с сортировкой — идеально. 👌

Кейс 2: сравнение текущей и предыдущей суммы

Когда нужно вычислить, насколько изменился заказ клиента:

SELECT *,
amount - LAG(amount) OVER (PARTITION BY client_id ORDER BY order_date) AS delta
FROM orders;


Здесь мы с помощью LAG() смотрим назад и сравниваем текущую сумму с предыдущей.

Очень часто нужно в продуктовой аналитике или для alert'ов.

Топ-4 самых частых оконных функций:

🔺ROW_NUMBER() — уникальный номер строки в группе
🔺RANK() — ранг с пропусками
🔺DENSE_RANK() — ранг без пропусков
🔺LAG() / LEAD() — посмотреть назад/вперёд

Оконки — это не только для data analysts.
Data Engineers используют их в витринах, для дедупликации, расчёта активных пользователей, историзации и других задач.
Меньше подзапросов — чище код.

А ты уже используешь оконки?
😎 Да, активно применяю
🤔 Пока только смотрю и не трогаю
Please open Telegram to view this post
VIEW IN TELEGRAM
😎23👍11🤔6
#мысли

😵 Не понимаю SQL — и что?

Когда я только устраивался в первую команду, за плечами у меня был всего один курс по SQL на Stepik.

Я выучил SELECT, WHERE, GROUP BY, чуть-чуть JOIN — и почувствовал, что этого мало.
Пошёл в документацию PostgreSQL…
И честно? Почти ничего не понял.

Куда смотреть? Какие функции читать? Что вообще важное для работы, а что пока рано?
Я залипал на статьях, не запоминал примеры, перегружался.

И вот что я тогда понял:

▫️ Не надо пытаться выучить всё сразу.
▫️ Не надо читать теорию «про запас».

⬜️ Есть задача → думай, как её решить.
⬜️ Чувствуешь, что не хватает чего-то → гугли, лезь в доку точечно.
⬜️ Применяешь — запоминаешь. Видишь результат — растёшь.

💡 Работа от задачи — самый надёжный способ учиться.
Ты не обязан понимать оконные функции, если ещё не сталкивался с задачей по «поиску последних записей».
Ты не обязан лезть в аналитические витрины, если не знаешь даже, как посчитать суммы по группам.

Начни с малого.
Один день — одна команда.
Одна задача — одно решение.

А ты как учишь SQL?

🔥 От задачи к коду
👍 Сначала читаю теорию

P.S. Подходы применимы везде. Если у вас есть рабочие техники и приемы, поделитесь в комментах 😀
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥22👍124👌1
#вопрос

JOIN или EXISTS — что выбрать?

У тебя есть две таблицы, и нужно проверить, есть ли для строки в одной таблице соответствие в другой.

Ты что используешь:

-- EXISTS
SELECT *
FROM orders o
WHERE EXISTS (
SELECT 1 FROM clients c WHERE c.client_id = o.client_id
);

-- или JOIN?
SELECT o.*
FROM orders o
JOIN clients c ON c.client_id = o.client_id;


На первый взгляд — оба варианта работают. Но есть нюанс.

Несколько советов, если решишь попробовать EXISTS:

🔺EXISTS не возвращает данные — только говорит: «да, строка есть».
🔺Работает быстрее, потому что останавливается на первой найденной строке.
🔺Помогает избежать дублей, которые часто ловишь в JOIN.

👉 Если нужен только факт, что клиент существует — EXISTS часто эффективнее.

А если нужно подтянуть данные — тогда JOIN.

P.S. Я сам долго не пользовался EXISTS, пока не начал ловить дубли в отчётах.

А ты как выбираешь?

🔥EXISTS — если нужна проверка
😎 JOIN — всегда привычнее

Персонажей делал сам, чтобы их образ был максимально подходящим.
Please open Telegram to view this post
VIEW IN TELEGRAM
😎16👍7🔥6
#путь_de

🤦‍♂️ Как я однажды заджойнил не туда

Вроде бы простая задача — соединить клиентов с заказами.
Берём client_id, пишем JOIN, получаем отчёт…

А дальше — что-то пошло не так.

В отчёте вдруг стало в 4 раза больше клиентов, чем должно быть.
Такого числа пользователей в системе быть просто не могло.

Начал копать:
🔹таблица с клиентами оказалась без фильтрации по активным,
🔹плюс дубли, которые я не удалил,
🔹плюс забыл, что в таких случаях нужно many-to-one, а не many-to-many.

С тех пор прежде чем джойнить — думаю:

🔺Какой ключ?
🔺Это точно one-to-one или one-to-many?
🔺Нужна ли фильтрация или дедупликация?

И главное — понимать бизнес-смысл таблицы, а не просто “ну тут вроде client_id есть”.

🔸 Итог: таблица похудела, отчёт стал вменяемым, и я запомнил это надолго.

А у тебя бывали такие косяки, когда отчёт “раздувался до луны”? 🤯
Расскажи в комментах 👇
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11😁21
#база_знаний

ROWS vs RANGE — тонкая грань при работе с датами


Друзья, привет, с новой рабочей неделей
Сегодня поделюсь небольшим на первый взгляд, но существенным нюансом при работе с ранжированием по датам оконками. Несколько раз сам попадал на этом 😅

🔸В чём суть?

ROWS — работает построчно, на уровне физического порядка.
RANGE — работает по значениям в ORDER BY. Особенно опасно с датами и числами с повторами.

SELECT
order_id, order_date,
amount,
SUM(amount) OVER (
ORDER BY order_date
RANGE BETWEEN INTERVAL '7 day' PRECEDING AND CURRENT ROW
) AS week_sum
FROM orders;


Звучит как: "посчитать сумму за последние 7 дней". Но RANGE ищет все строки с order_date в пределах интервала, а не 7 последних строк. Если у тебя несколько заказов в один день, все они попадут.

А теперь то же с ROWS:

SUM(amount) OVER (
ORDER BY order_date
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
)


Теперь считаем ровно 7 последних строк — вне зависимости от того, в какой день они были. Это скользящее окно по строкам, не по датам.

🔸Подвохи:

RANGE работает только с одноуровневым ORDER BY.
(нельзя ORDER BY col1, col2 + RANGE в PostgreSQL)
RANGE — может "захватывать" слишком много строк, если в один день много событий.

ROWS — проще, детерминированно, часто предпочтительнее.

🔸Что выбрать?

Если хочешь чёткое "n последних строк" → ROWS

Хочешь "всё, что в пределах X дней/часов" → RANGE INTERVAL ...

Но проверь, как оно работает в твоей СУБД. В PostgreSQL — RANGE INTERVAL появился с версии 14, если не ошибаюсь.

Ты знал об этом различии? Используешь RANGE в проде? (Я не использую 🙂‍↔️).

👍 - конечно знаю про различие
🔥 - не знал, но теперь все схвачено
🔥15👍7👌1
#мысли

💭
Честность с собой в обучении

Раньше я проходил курсы просто для галочки, чтобы был сертификат. Думал, что он подтверждает наличие у меня знаний.
Сертификат — есть. Глава — закрыта. Всё

🤔 В этом, кстати, есть своя логика. На старте хочется набить портфолио «корочками», чтобы хоть как-то зацепиться.

Попадаешь на проект — и вскоре понимаешь:
никому не интересны твои сертификаты.
Важна только одна вещь — насколько глубоко ты реально в теме.

📚 Сейчас я стараюсь по-другому подходить к обучению:

🔹Когда смотрю видео — я лезу в теорию глубже.
🔹Если читаю про SQL-функции — проверяю, как они работают на моих данных, генерирую тестовые данные.
🔹Если вижу паттерн в чужом коде — пытаюсь воспроизвести и осознать его логику.

Когда ты так нарабатываешь опыт, то начинаешь видеть закономерности, и в этом смысле — обучение становится не задачей в таск трекере, а способом думать. Типовые задачи становятся частью большой системы, элементы которой ты идентифицируешь и уже знаешь, какие подходы применять.

А как у тебя? Учишься «для галочки» или стараешься впитать по-настоящему?

— Поделись, как ты понял, что знания начинают работать.
🔥7💯5👍3
#вопрос

Как найти клиентов с двумя и более заказами в день?

Задача с собеседования, которая звучит просто, но часто ломает логику:

У нас есть таблица orders(client_id, order_date).
Как найти клиентов, которые сделали два и более заказа в один день?

🧩 На первый взгляд — просто:
SELECT
client_id,
order_date,
COUNT(*) AS cnt
FROM orders
GROUP BY client_id, order_date
HAVING COUNT(*) > 1;


Но на практике это решение работает только в «стерильной» базе. В боевых проектах — много подводных камней.

⚠️ Нюансы, которые ловят даже опытных

1. order_date — это timestamp, а не date
2. Могут быть дубликаты заказов
3. Могут быть разные временные зоны
4. Не все статусы считаются заказом (если есть доп поле status_id)

⭐️ Доп. вопросы, чтобы проверить глубину знаний:

А как найти клиентов с 2+ заказами подряд в течение суток?
А если нужны не просто 2+, а все даты, где было превышение?
А как посчитать, сколько таких дней было у клиента?

На задачах типа «2+ событий в день» можно отлично прокачать:
- работу с датами и временными зонами,
- агрегации,
- оконные функции,
- производительность группировок на больших объёмах.

👇 А ты разбирал такие задачи?
Стоит потренироваться. А то написал запрос, а потом нашел 100к лишних строк.
Если интересен формат задач с собесов, ставь огонек 🔥
🔥46
#база_знаний

Мои топ-3 SQL-конструкции, без которых я реально не представляю рабочие дни.


Друзья, доброго понедельника. Неделю хочу начать, по традиции, с базы.

Вот простые на первый взгляд штуки, но на них держится очень многое: витрины, отчёты, фичи для моделей и пайплайны.

🔹 CASE WHEN - любимчик
Незаменим, когда нужно сделать pivot — развернуть категории в столбцы.
Например, посчитать агрегаты отдельно по каждому сегменту клиента:
SUM(CASE WHEN segment = 'A' THEN amount ELSE 0 END) AS segment_a_amount


Часто готовлю так фичи для дата-сайентистов или витрины для BI. Очень гибко и удобно.

🔹 COALESCE()
Мой спасатель от NULL'ов. 🙌
Когда соединяешь основную таблицу с внешними справочниками — всегда есть риск наткнуться на пустые значения.
COALESCE(discount_percent, 0) AS applied_discount


Если не поставить 0, потом все расчёты по скидкам могут поехать.
А так — надёжно и предсказуемо 😄

🔹 SUBSTRING() + DATE_TRUNC()
Когда даты приходят криво или просто в текстовом формате — спасаюсь связкой SUBSTRING и DATE_TRUNC.
Например, для группировки событий по месяцам:
SELECT
SUBSTRING(DATE_TRUNC('month', created_at)::text, 1, 7) AS year_month
FROM events;


🛠 Это мои рабочие кони каждый день.

А у тебя какие конструкции самые ходовые? (SQL / Spark SQL) — расскажи! 👇
9👍9🔥4
#мотивация

Учеба - это череда маленьких побед. Кажется, что все будет линейно, но большинство знает, что это провалы и восхождения, процесс крайне нестабильный. И когда ты подступаешься к очередной теме в SQL, Spark или любой другой в DE, ты думаешь "А как это понять, как выучить?"

И главная мысль в том, что учёба в области DE нужна постоянно. Это не покорение одной большой горы, а маленькие победы каждый день. Одна задача. Один новый паттерн, одно новое понимание.

Сегодня ты разобрался, что идет первым, WHERE или GROUP BY.
Завтра - написал нормальный JOIN с ROW_NUMBER().
Послезавтра - построил ETL в Spark, от источника до BI.

И всё это складывается (если постоянно практиковать).

Уже через какое-то время тебе говорят, что ты не просто хорошо пишешь код, а неплохо оптимизируешь запросы, иди ка подскажи!

🌱 Не пытайся обогнать всех. Просто расти каждый день по 1%. И так в любой области кстати: профессия, спорт, хобби.

Я сейчас вспоминаю python, решая каждый день по 10 задач. Беда в том, что знания быстро уходят, если их не применять. Поэтому иногда необходимо создавать для себя искусственный полигон.

👇 Расскажи, какую маленькую победу ты помнишь.
💯154🔥4👍1
#вопрос

Как найти “возвраты” клиента в течение 7 дней?


Вижу, что вам понравился формат задач с собесов, тогда продолжаем.

📆 Представь, что ты смотришь на таблицу заказов и хочешь понять:
а кто из клиентов вернулся за покупкой в течение недели после предыдущего заказа?

Вроде простая, но с подводными камнями.

WITH client_orders AS (
SELECT
client_id,
order_date,
LEAD(order_date) OVER (
PARTITION BY client_id ORDER BY order_date
) AS next_order
FROM orders
)
SELECT
client_id,
order_date,
next_order
FROM client_orders
WHERE next_order IS NOT NULL
AND next_order <= order_date + INTERVAL '7 days';

🙄 Что здесь происходит:

Мы берём текущий заказ и смотрим вперёд (через LEAD) — был ли следующий и когда.
Сравниваем даты: если следующий заказ был в течение 7 дней → это повтор. Фильтрация отсеет всё, кроме недельных интервалов.

Но вот теперь начинаются вопросы, с которыми валили кандидатов:

1. А если у клиента было 3 заказа подряд с интервалом 2 дня — как будет считаться?
2. А если два заказа в один день? LEAD их правильно выстроит?
3. Что делать с отменёнными заказами?
4. Нужно ли нормализовать order_date до DATE(order_date) (а если там timestamp)?
5. Можно ли вместо LEAD() сделать LAG() и считать "возврат" наоборот — от следующей даты назад?
6. А если нужно подсчитать долю клиентов, которые вернулись в течение 7 дней?

📌 Такие задачи хороши тем, что они простые на старте — но требуют понимания оконных функций и аккуратного отношения к данным.

Я так смотрю, что почти все задачи такого формата идут с расчетом на применение оконок.

В копилку задачек для тренировки 💪🏻

А ты бы как решил? А доп вопросы? Традиционно, 🔥 если заходит.
🔥25💯21
#база_знаний

🔥 Боль UNPIVOT в Greenplum

Всем привет после майских — пора включаться в рабочие процессы, как бы тяжело это ни было 😅

Вспомнил один рабочий кейс с проекта, который в своё время стал для меня настоящим инсайтом. Я готовил данные для BI дашбордов и отдал таблицу, которая сломала все графики. Все полетело к черту, а я гадал "а как же сделать правильно и не руками?".

Тогда впервые понял, как простой CASE + VALUES может заменить весь UNION ALL и спасти от 50 строк повторяющегося кода.

Пример условный, чтобы понять механику.
report_date category clicks views purchases
2024-05-01 sport 120 350 5
2024-05-01 tech 200 500 12


Визуально выглядит хорошо, но для построения отчетов - плохо. Гибкости ноль. Нужно, чтобы в BI автоматически с применением нужного фильтра отчет сам перестраивался. А для этого нужно сделать поле subcategory:
report_date category subcategory value
2024-05-01 sport clicks 120
2024-05-01 sport views 350
2024-05-01 sport purchases 5


То есть свернуть все поля (сделать обратный pivot) до одного поля, а дополнительно создать поле value.

🛠 В Greenplum UNPIVOT напрямую не поддерживается, но решение есть — и оно оказалось очень элегантным:
SELECT
report_date,
category,
subcategory,
CASE subcategory
WHEN 'clicks' THEN clicks
WHEN 'views' THEN views
WHEN 'purchases' THEN purchases
END::numeric AS value
FROM metrics,
(VALUES ('clicks'), ('views'), ('purchases')) AS sub(subcategory);


С тех пор — я фанат CASE.
(Кстати, о CASE я писал отдельно 👉 тут)

💡 Мораль:
PIVOT удобно для человека.
UNPIVOT удобно для системы.

🔽 А ты сталкивался с таким?
👍 — Знал об этом трюке
🔥 — Не знал, и это было полезно

Если работаешь с BI — сохрани этот паттерн.
Однажды он тебе точно пригодится.
🔥18👍3
#база_знаний

📆 Пропущенные даты в активности (поиск дыр)


Когда вы анализируете поведение пользователей, часто смотрите на дни активности — когда был заказ, вход в приложение или другое событие.
Но бывает важно сделать наоборот: а в какие дни активности не было?

Например, из всего календарного месяца нужно найти даты, когда клиент ничего не делал. Это помогает выявить падение интереса, возможный churn или просто дыры в трекинге.

Временные ряды — как дополнение к практике

Я сам начал работать с временными рядами только в Spark.
Но это прям классный приём, Джуны, берите на вооружение:
создаёте “идеальный” календарь — и сравниваете его с реальными действиями пользователя.

Кейс в Greenplum / Postgres

WITH calendar AS (
SELECT generate_series('2024-01-01', '2024-01-31', INTERVAL '1 day') AS day
),
activity AS (
SELECT DISTINCT DATE(order_date) AS active_day
FROM orders
)
SELECT day
FROM calendar
LEFT JOIN activity ON day = active_day
WHERE active_day IS NULL;


А если в Spark?

WITH calendar AS (
SELECT explode(sequence(to_date('2024-01-01'), to_date('2024-01-31'), interval 1 day)) AS day
),
activity AS (
SELECT DISTINCT to_date(order_date) AS active_day
FROM orders
)
SELECT day
FROM calendar
LEFT JOIN activity ON day = active_day
WHERE active_day IS NULL;


В Spark нет generate_series, но sequence + explode отлично решает задачу.

🤔 Зачем это использовать?
• Находить дни без активности.
• Проверять трекинг событий (вдруг что-то не пишется?).
• Построить нормальные графики в BI — без пропусков по датам.
• Делать анализ удержания и активных дней.

Если хочешь потренироваться, попробуй адаптировать запрос под клиента — чтобы находить “пустые” дни для каждого client_id.
Это уже уровень middle, но отличная тренировка мышления “как аналитик”.

Если интересно — напишу разбор с решением! Но с вас реакции! Сохраняйте материал.
👍192👌1
☝🏻Полезный SQL-приём: NULLIF
Совсем недавно узнал такой неочевидный прием, что когда нужно посчитать что-то вроде:
amount / clicks


... бывает, что clicks = 0, и тогда произойдёт ошибка деления на ноль.
Стандартный способ — использовать CASE:
CASE
WHEN clicks = 0 THEN NULL
ELSE amount / clicks
END


Это правильно, но громоздко.
Есть короткий способ — функция NULLIF:
amount / NULLIF(clicks, 0)


Как работает:
🔸 NULLIF(clicks, 0) вернёт NULL, если clicks = 0
🔸 Если clicks ≠ 0, то вернёт само значение clicks

А в SQL, как известно:
Любая операция с NULL даёт NULL, но не вызывает ошибку.


То есть:
🔸 если clicks = 5, то amount / 5
🔸 если clicks = 0, то amount / NULL → результат будет NULL, без ошибки

Это удобно, если тебе важна устойчивость запроса:
🔸 ты избегаешь падения из-за деления на ноль
🔸 а при этом не теряешь логику: нулевые значения просто будут как NULL

Когда использовать:
🔸 при расчётах CTR, CPC и других метрик с делением
🔸 в агрегатах: AVG(amount / NULLIF(clicks, 0))
🔸 в отчётах, где важна стабильность

Кажется, что функция довольно полезна, но ее потенциал для себя мне предстоит раскрыть!

Ты используешь NULLIF в своих запросах?
Может, есть другие короткие трюки, которые часто выручают?
🔥19👍2👌1