Telegram Web
Привет, делюсь папкой дата инженерных каналов с интересным контентом.
Заходи к нам на вечеринку по ссылке: https://www.tgoop.com/addlist/a1B07iwrPxUxNWIy
#вести_с_полей

Напоминалка про NULL в sql

Null не содержит значения (мы не знаем, какое значение атрибута было "в том месте и в то время"),
но хранит тип данных.

На продакшене, если добавляешь новую колонку "на будущее" или гармонизируешь данные из разных источников, вместо
select ...
, null as new_int_col
, null as new_date_col
, null as new_ts_col

стоит писать
select ...
, (cast null as integer) as new_int_col
, (cast null as date) as new_date_col
, (cast null as timestamp with time zone) as new_ts_col

Иначе жди проблемы интеграций "ниже по потоку" или при union all'ах.

p.s. Ну и помни, что в SQL boolean может иметь три значения, и null != False != True :)
#анонс

Boosty-цель выполнена — стриму с разбором резюме быть

В субботу 08.02.25, в 19:00 мск, я провёл стрим на твиче на 2.5-3ч.
UPD: Запись уже лежит на youtube

🔸 Сильное резюме почти гарантирует возможность дойти до тех. собеса и показать свои навыки. Если ты не ищешь работу сейчас, оно добавит спокойствия на случай резких перемен в жизни (увольнение, переезд и т.д.). Или поможет улучшить условия труда и повысить ЗП.

🔸 Присылай своё резюме заранее, буду проходить по стопке "снизу-вверх", разбирая очередь. Закинуть на стриме тоже можно будет, но я не обещаю успеть всё. Планирую до 10 минут на 1 резюме, почти наверняка в "одностороннем режиме", без диалога.

🔸 Почему моё мнение может быть полезным:
. HR сами пишут мне в личку, даже когда я в пассивном поиске с фильтром "от 400к на руки"
. через меня в роли ментора прошло более 50 человек. Скину в комменты обратную связь от некоторых из них по результатам доработки резюме
. в 2024 я получил 4 Sr. DE оффера на 500к+ на руки (в том числе с релокацией на Кипр)
. сам отсобеседовал 10+ человек на позиции middle, senior

UPD: приём резюме закрыт

Моё актуальное резюме всегда доступно здесь
#вести_с_полей

Чтобы распарсить SQL, нужно всего лишь написать правильную регулярку. Правда?

🔸 Я на трёх проектах пробовал решить задачу "программно прочитать SQL код и построить по нему `Data Lineage`". В том числе используя разные python пакеты вроде Sqlparse и Sqlglot. Цель более-менее сводилась к "хочу знать зависимости для группировки объектов на стадии миграции". Если знаешь, что везти сначала, а что -- потом, это делает планирование проекта прозрачным, а сроки -- предсказуемыми. В общем, важное и крутое знание для менеджмента, которое можно получить анализом проекта.

🔸 Вначале с готовыми пакетами всё неплохо, OLTP запросы проходят все тесты. Но потом начинаются разные особенности OLAP запросов, и небо "затягивает тучами":
. CTE и подзапросы
. десятки JOIN'ов
. не ANSI-SQL особенности диалекта СУБД на проекте
. функции (UDF) и хранимые процедуры (SP), которые перешли в виде "наследства"

🔸 Сегодня на почту упала небольшая статейка как раз на эту тему: "3 уровня понимания SQL". В ней описывают термин "`SQL Comprehension`" -- распознавание SQL кода, которое можно реализовать на разной глубине:
1. Парсинг -- проверка валидности SQL Keywords, количества аргументов в функциях
2. Компиляция -- построение логического плана с учётом типов данных колонок
3. Выполнение -- физический план + результат запроса, учитывает особенности СУБД

Рекомендую пробежаться по примеру и тому, как и что можно сломать :)

p.s. спасибо dbt, что он есть, и UDF & SP можно встречать сильно реже
p.p.s. любая СУБД "понимает" SQL на 3м уровне, да
p.p.p.s. очень любопытно, что получится у dbt после поглощения SDF и использования его как движка. Неужели то самое светлое будущее наступит для analytics engineers?
#реклама

Делюсь ещё одним DE каналом, который можно почитать во время затишья на этом :)

Один из авторов описывает так:
"
На канале @DataEngineeringDigest мы с командой отбираем лучшие доклады с профильных конференций по DE, смотрим их и делаем конспект, который оформляем в пост. Канал помогает сэкономить время: вместо того чтобы смотреть часовые видео с конференций, вы можете прочитать краткий и ёмкий пересказ.

Выделяем ключевые идеи, технические детали и практические выводы из докладов. Конференции - лучший способ для профессионального развития, ведь там рассказываются реальные проблемы и способы их решения)
"
#поразмыслим

В чём разница между этими двумя вариантами для отладки кода на деве и миграций на проде?

1.
create table if not exists <table_name> (id int, ...);


2.
drop table if exists <table_name>;
create table <table_name> (id int, ...);
#вести_с_полей

Чтобы распарсить SQL, нужно всего лишь написать правильную регулярку. Правда? (Часть 2) 1/2

🔸 Попробовал sqllineage — python пакет для синтаксического парсинга аналитического SQL кода. Крутая штука, спасибо @Vasiliy_Balkichev из комментов за рекомендацию)

https://sqllineage.readthedocs.io

Применил для рабочей задачки, делюсь синтетическим примером.
Нужно было создать ``dbt`` модельки на основе .sql файлов. Допустим, у нас есть такой SQL

sql = """
WITH order_summary AS (
SELECT
o.order_id,
o.customer_id,
o.order_date,
SUM(oi.quantity * p.price) AS total_amount,
COUNT(oi.product_id) AS items_count
FROM sales.orders o
JOIN sales.order_items oi ON o.order_id = oi.order_id
JOIN warehouse.products p ON oi.product_id = p.product_id
WHERE o.order_date >= '2023-01-01'
GROUP BY o.order_id, o.customer_id, o.order_date
),
customer_segments AS (
SELECT
customer_id,
name,
signup_date,
CASE
WHEN total_purchases > 10000 THEN 'Premium'
WHEN total_purchases > 5000 THEN 'Gold'
WHEN total_purchases > 1000 THEN 'Silver'
ELSE 'Bronze'
END AS customer_tier
FROM customers
)
SELECT
cs.customer_id,
cs.name,
cs.customer_tier,
AVG(os.total_amount) AS avg_order_value,
SUM(os.total_amount) AS lifetime_value,
COUNT(os.order_id) AS order_count,
MAX(os.order_date) AS last_order_date,
ROW_NUMBER() OVER (PARTITION BY cs.customer_tier ORDER BY SUM(os.total_amount) DESC) AS tier_rank,
SUM(os.total_amount) / SUM(SUM(os.total_amount)) OVER (PARTITION BY cs.customer_tier) * 100 AS pct_of_tier_revenue
FROM customer_segments cs
LEFT JOIN order_summary os ON cs.customer_id = os.customer_id
GROUP BY cs.customer_id, cs.name, cs.customer_tier
HAVING COUNT(os.order_id) > 3
ORDER BY cs.customer_tier, lifetime_value DESC;
"""
Чтобы распарсить SQL, нужно всего лишь написать правильную регулярку. Правда? (Часть 2) 2/2

Тогда при помощи такого кода мы можем найти все ссылки на таблицы-источники и заменить их на {{ source() }} макрос

import re
from sqllineage.runner import LineageRunner

DEFAULT_SCHEMA = "stg"

runner = LineageRunner(sql, dialect="trino") # added dialect

result_sql = sql
for table in runner.source_tables:
table_str = str(table)
parts = table_str.split(".")
table_name = parts[-1]

# Determine schema
if parts[-2] == "<default>":
schema_in_sql = DEFAULT_SCHEMA
# Match both standalone table and table
pattern = rf"(from|join)\s+{re.escape(table_name)}"
else:
schema_in_sql = parts[-2]
# Match both schema.table and schema.table
pattern = rf"(from|join)\s+{re.escape(schema_in_sql)}\.{re.escape(table_name)}"

# Find all matches with case insensitivity
for match in re.finditer(pattern, result_sql, re.IGNORECASE):
original = match.group(0)
keyword = match.group(1) # FROM or JOIN
replacement = f"{keyword} {{{{ source('{schema_in_sql}', '{table_name}') }}}}"

# Replace just this specific occurrence
result_sql = result_sql.replace(original, replacement, 1)

print(result_sql)


Обрати внимание, что корректно обработаны названия CTE, случаи с упоминанием таблицы без схемы.

p.s. без регулярок не обошлось, потому что результат парсинга не связан с исходным SQL кодом, и для замены нужно найти верное место. А может и я не до конца разобрался :)
Изменения в менторской программе: теперь с любого уровня до закрытия испыталки

🔸 Я перехожу на модель "доведение до конца испытательного срока" вместо разовых консультаций. Это позволит обеспечить более глубокую поддержку и ответственный подход к карьерному развитию менти.

Ключевые изменения:
- Шпаргалка будет доступна текущим пользователям boosty до 21 марта, потом закрывается, дополняется и трансформируется в материалы подготовки
- Новая структура оплаты: предоплата + процент от оффера
- Регулярная обратная связь от меня и мотивация на движении к цели
- Детально структурированный процесс обучения с пет-проектом на приближенной к реальности инфраструктуре

upd: Теперь есть опция для действующих DE: повышение ЗП, предоплата + процент от увеличения ЗП

🔸 Если это именно то, чего не хватало тебе в предыдущем подходе, — смотри детальные условия на новом лендинге и пиши в личку https://rzvde.tilda.ws/

Задавай вопросы в комменты, обсудим

Изменения направлены на повышение качества поддержки и увеличение процента успешных переходов в DE.
27го марта в ~19:00 мск буду выступать на dbt митапе.
Регистрируйся и подключайся слушать, если такое интересно)

Тема: "Так ли удобен dbt, как о нём рассказывают (костыли, ограничения, трудности)"

https://inzhenerka.tech/dbt_meetup

p.s. запись будет
#рекомендую

Вот такую песочницу на docker compose для домашнего изучения Big Data стека сделал Сергей, один из подписчиков. Может хорошо сочетаться с Halltape Roadmap, о котором я писал раньше.

https://gitflic.ru/project/ananevsyu/sandbox_db_public

"Чтобы у людей была возможность удобно учиться"

Насыпь ему звёздочек в репо и отпишись в комментах, если тебе окажется полезным ;)
#вести_с_полей

Как dbt retry может сохранять менталку при локальной отладке кода

Оказывается, начиная с версии ~1.8 в dbt появилась команда
dbt retry,
которая повторяет предыдущую команду для всех "упавших" и "пропущенных" моделей и тестов.

Посмотри, какая красота

upd: основное — в первой и последней строчках на "скриншотах"
Я приду и подключусь к обсуждению, а ты?
Я построю свое DWH с блекджеком и шлюпками

23 марта (ВС) 20:00 МСК

Созваниваемся в Яндекс Телемосте. Я при вас рисую в Miro архитектуру DWH, а потом говорю, где тут могут быть проблемы. Дальше поставлю ТЗ по данным и мы попробуем покидать идеи, как сделать максимально компактно и без веренницы технологий.

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

Зачем я это делаю?
Хочу разобраться, почему сразу нельзя построить универсальное хранилище для любой команды и компании?

Если вы бывалый в DE, то приглашаю присоедениться и смело высказать свое мнение.

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


📅 Дата: 23 марта 2025
Время: 20:00 МСК
📍 Где: Яндекс Телемост - ССЫЛКА НА ВСТРЕЧУ

Подписывайтесь, чтобы не пропустить. Будет полезно, без воды и максимально по делу.
Please open Telegram to view this post
VIEW IN TELEGRAM
Мок-собесы в СНГ вернулись, второй сезон!

Дабл-дроп видеозаписей в обновлённом формате.
Первое, про облака на джун+ - миддл
Второе про всё подряд на сеньора

🔸 Смотри
🔸 Тренируйся отвечать на вопросы
🔸 Комментируй
🔸 Делись с другими
🔸 И иди на собесы самостоятельно!

Пиши, если тоже хочешь потренироваться
Теперь бесплатно


S2E1 посмотри обязательно, чтобы увидеть что даже не на 100% готовым к собесам можно на них выйти и начать нарабатывать навык их прохождения

S2E2 посмотри обязательно, чтобы услышать первоклассный подход к ответам на незнакомые вопросы и рассуждения на основе опыта
Впервые за 6 лет узнал об этой конструкции SQL

upd: расходимся, это не ANSI-SQL. не рекомендую вставлять на проект, потому что однажды точно случится миграция

is distinct from
позволяет сравнивать значения с учётом null. Если одно из значений — null, значит другое not-null значение не будет ему равно. Ведёт себя немного иначе, по сравнению с = null, is null.
col1 is distinct from col2


Более стандартный подход:
CASE WHEN (col1 = col2) or (col1 IS NULL AND col2 IS NULL)
THEN false
ELSE true
END


Ну и код чтобы поиграться локально на postgres (см. картинку):
with my_data as (
select null as col
union all
select 'null'
)
select
col
,col = null as c1
,col = 'null' as c2
,col != null as c3
,col != 'null' as c4
,col is null as c5
,col is not null as c6

,col is distinct from null as c7
,col is distinct from 'null' as c8
,col is not distinct from null as c9
,col is not distinct from 'null' as c10
from my_data
Рассказал, объяснил про костыли в DBT.
—> Видео выложено на youtube

Outline
нельзя писать в одну таблицу из разных мест
* наивный подход
* с учётом duckdb адаптированного кода
* с учётом зависимостей
* а если нужна разная логика по источникам

работа с макросами
* список, return и {{ }}
* {{ log() if execute }}
* джсон ‘’ и “ ”

юнит-тесты и кодогенерация
* пример конфига
* пример простой модельки генерации метрик
* во что это превращается с юнит тестами

монорепо vs разные репо
* на примере dbt docs
* только важные связи между моделями


—> Послушать остальные доклады
2025/04/07 01:06:56

❌Photos not found?❌Click here to update cache.


Back to Top
HTML Embed Code: