Привет дорогой подписчик!
Я был в отпуске 🏝️
с завтра посты будут выходить вновь регулярно
По понедельникам змеиные трюки #PyTrickMonday
По средам разбираем SQL #SQLWednesday
И конечно большие разборы интересных тем😄
Я был в отпуске 🏝️
с завтра посты будут выходить вновь регулярно
По понедельникам змеиные трюки #PyTrickMonday
По средам разбираем SQL #SQLWednesday
И конечно большие разборы интересных тем
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥7👏1
#PyTrickMonday
С началом новой недели, друзья!🔥
Сегодня разбираем одну из самых «незаметных», но реально полезных фич - паттерны с #enumerate и #zip.
Уверен вам всем знаком трюк с enumerate:
- start=1 - чтобы начать нумерацию с единицы (по умолчанию с 0).
- Избавляет от классического костыля с range(len(...)).
❓ А можно ли параллельно итерироваться по двум спискам ? zip()
- Если один список короче - лишние элементы просто отбросятся.
- Чтобы «паддинг» сделать, можно использовать itertools.zip_longest.
А если надо по трем спискам учитывать индекс и еще и за один проход цикла?
- Оборачиваем zip в enumerate, чтобы сразу иметь и счётчик, и все значения.
- Кортеж (pair, price, vol) можно сразу «распаковать».
Где же использовать такую комбинацию zip + enumerate?
1. Самое частое где я пользовался этим в разработке это конечно тесты чтобы сопоставить тест кейс и результат.
2. Логика с порогами для разных метрик идеально вообще
Нумерация для читаемого лога и сразу три колонки в цикле.
1. enumerate экономит строчки и бережёт от ошибок с «ручным» счётчиком.
2. zip позволяет синхронно итерировать сразу несколько коллекций.
Перешлите коллегам, пусть тоже прокачают свой Python-код! 😉
#python #codehacks #itertools
С началом новой недели, друзья!
Сегодня разбираем одну из самых «незаметных», но реально полезных фич - паттерны с #enumerate и #zip.
Уверен вам всем знаком трюк с enumerate:
items = ["BTC", "ETH", "SOL"]
for idx, symbol in enumerate(items, start=1):
print(f"{idx}. {symbol}")
- start=1 - чтобы начать нумерацию с единицы (по умолчанию с 0).
- Избавляет от классического костыля с range(len(...)).
pairs = ["BTCUSDT", "ETHUSDT", "SOLUSDT"]
prices = [100000, 3000, 56] #цена выдумана любое совпадение случайно
for pair, price in zip(pairs, prices):
print(f"{pair}: {price}$")
- Если один список короче - лишние элементы просто отбросятся.
- Чтобы «паддинг» сделать, можно использовать itertools.zip_longest.
А если надо по трем спискам учитывать индекс и еще и за один проход цикла?
dates = ["2025-06-30", "2025-07-01", "2025-07-02"]
volumes = [1234, 2345, 3456]
pairs = ["BTCUSDT", "ETHUSDT", "SOLUSDT"]
prices = [100000, 3000, 56] #цена выдумана любое совпадение случайно
for i, (pair, price, vol) in enumerate(zip(pairs, prices, volumes), start=1):
print(f"{i}) {pair} — {price}$, объём {vol}")
- Оборачиваем zip в enumerate, чтобы сразу иметь и счётчик, и все значения.
- Кортеж (pair, price, vol) можно сразу «распаковать».
Где же использовать такую комбинацию zip + enumerate?
1. Самое частое где я пользовался этим в разработке это конечно тесты чтобы сопоставить тест кейс и результат.
inputs = [2, 4, 6]
outputs = [4, 16, 36]
for test_num, (inp, outp) in enumerate(zip(inputs, outputs), 1):
assert my_func(inp) == outp, f"Fail on test #{test_num}"
2. Логика с порогами для разных метрик идеально вообще
metrics = ["CPU", "RAM", "Disk"]
values = [75, 60, 90]
limits = [80, 70, 85]
for i, (m, v, lim) in enumerate(zip(metrics, values, limits), 1):
status = "OK" if v < lim else "ALERT"
print(f"{i}) {m}: {v}% (limit {lim}%) — {status}")
Нумерация для читаемого лога и сразу три колонки в цикле.
1. enumerate экономит строчки и бережёт от ошибок с «ручным» счётчиком.
2. zip позволяет синхронно итерировать сразу несколько коллекций.
Перешлите коллегам, пусть тоже прокачают свой Python-код! 😉
#python #codehacks #itertools
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
#SQLWednesday
Привет, сейчас середина недели а значит время SQL и сегодня рзберем задачку с собеседования❓
Задача звучит так:
Найти топ-3(N) продаж (Top N Records per Group) для каждого продукта.
Дано табличка Sales:
Нужно вывести для каждого product_id три записи с наибольшей sale_amount. Как это сделать ? Конечно с помощью оконной функции ROW_NUMBER()
1. Сначала делаем запрос с оконкой чтобы получить номера.
- ROW_NUMBER() OVER (PARTITION BY product_id ORDER BY sale_amount DESC)
- Партиционируем по продукту и сортируем от большего к меньшему сумму так мы получим по каждосу продукту номер в зависимости от суммы продажи.
2. Делаем завпрос из верхнего подзапроса, фильтруя результат.
- В подзапросе уже ROW_NUMBER() присваивает уникальный порядковый номер каждой строке внутри группы product_id, отсортированной по убыванию sale_amount.
Результат:
Оконные функции вещь мощная, используйте ее для решения подобных задач!
Сохраняйте себе в закладки, пересылайте друзьям и делитесь своими альтернативными решениями в комментариях! 🙂
#DataEngineer #SQL #Interview #DataJungle
Испытание для Вас:
Задача:
Для каждого product_id и каждого месяца (`sale_date`) вывести топ-3 продаж по sale_amount.
💬 Поделитесь своим SQL-запросом в комментариях!
Привет, сейчас середина недели а значит время SQL и сегодня рзберем задачку с собеседования
Задача звучит так:
Найти топ-3(N) продаж (Top N Records per Group) для каждого продукта.
Дано табличка Sales:
| sale_id | product_id | sale_amount |
|---------|------------|-------------|
| S1 | P1 | 500 |
| S2 | P1 | 800 |
| S3 | P1 | 300 |
| S4 | P2 | 200 |
| S5 | P2 | 700 |
| S6 | P2 | 600 |
Нужно вывести для каждого product_id три записи с наибольшей sale_amount. Как это сделать ? Конечно с помощью оконной функции ROW_NUMBER()
1. Сначала делаем запрос с оконкой чтобы получить номера.
SELECT
product_id,
sale_id,
sale_amount,
ROW_NUMBER() OVER (
PARTITION BY product_id
ORDER BY sale_amount DESC
) AS rn
FROM Sales
- ROW_NUMBER() OVER (PARTITION BY product_id ORDER BY sale_amount DESC)
- Партиционируем по продукту и сортируем от большего к меньшему сумму так мы получим по каждосу продукту номер в зависимости от суммы продажи.
2. Делаем завпрос из верхнего подзапроса, фильтруя результат.
SELECT
t.product_id,
t.sale_id,
t.sale_amount
FROM (
SELECT
product_id,
sale_id,
sale_amount,
ROW_NUMBER() OVER (
PARTITION BY product_id
ORDER BY sale_amount DESC
) AS rn
FROM Sales
) AS t
WHERE t.rn <= 3;
- В подзапросе уже ROW_NUMBER() присваивает уникальный порядковый номер каждой строке внутри группы product_id, отсортированной по убыванию sale_amount.
Результат:
| product_id | sale_id | sale_amount |
|------------|---------|-------------|
| P1 | S2 | 800 |
| P1 | S1 | 500 |
| P1 | S3 | 300 |
| P2 | S5 | 700 |
| P2 | S6 | 600 |
| P2 | S4 | 200 |
Оконные функции вещь мощная, используйте ее для решения подобных задач!
Сохраняйте себе в закладки, пересылайте друзьям и делитесь своими альтернативными решениями в комментариях! 🙂
#DataEngineer #SQL #Interview #DataJungle
Испытание для Вас:
Добавим к нашей таблице `Sales` столбец `sale_date`:
| sale_id | product_id | sale_amount | sale_date |
|---------|------------|-------------|-------------|
| S1 | P1 | 500 | 2024-06-01 |
| S2 | P1 | 800 | 2024-06-05 |
| S3 | P1 | 300 | 2024-07-02 |
| S4 | P2 | 200 | 2024-06-10 |
| S5 | P2 | 700 | 2024-06-15 |
| S6 | P2 | 600 | 2024-07-01 |
Задача:
Для каждого product_id и каждого месяца (`sale_date`) вывести топ-3 продаж по sale_amount.
💬 Поделитесь своим SQL-запросом в комментариях!
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
#PyTrickMonday - уже не понедельник ну и что вы мне сделаете 🙂
С началом новой недели, друзья!🚀
Сегодня разбираем тему, которая реально прокачает читаемость вашего кода - аннотации типов и модуль #typing.
Зачем это нужно?
• 📈 IDE и линтеры (mypy) подсказывают ошибки ещё до запуска
• 🔍 Код самодокументируется - сразу видно, что функция ждёт и что возвращает
• 🤝 В командной разработке удобно понимать контракты без лишних комментариев
В примере выше вы видите достатчно простой пример аннотации типов, давайте рассмотрим более сложный вариант.
- List[float] вместо list — защита от случайных вставок строк
Tuple и Union для сложных структур
- Tuple[int, str] для жёсткого набора типов
- Union[X, Y] когда возможно несколько вариантов
TypedDict для диктов с фиксированным набором полей
- Помогает инструментам подсказывать поля и их типы. На самом деле вместо TypedDict можно использовать dataclass, рассказал как вот в этом посте.
Generic-функции и Protocols (продвинутый уровень)
- TypeVar делает функцию универсальной для любых типов.
Выводы:
- Аннотации приводят к более понятному и безопасному коду.
- Инструменты проверки (mypy) ловят ошибки раньше, чем ваш код упадёт.
- В командной работе это просто must-have!
Перешлите коллегам, пусть тоже прокачают свой Python-код аннотациями типов и вы перестанете гадать что же возвращает функция 😉
#python #typing #mypy #codehacks #cleanCode
С началом новой недели, друзья!
Сегодня разбираем тему, которая реально прокачает читаемость вашего кода - аннотации типов и модуль #typing.
def greet(name: str, times: int) -> None:
for _ in range(times):
print(f"Привет, {name}!")
Зачем это нужно?
• 📈 IDE и линтеры (mypy) подсказывают ошибки ещё до запуска
• 🔍 Код самодокументируется - сразу видно, что функция ждёт и что возвращает
• 🤝 В командной разработке удобно понимать контракты без лишних комментариев
В примере выше вы видите достатчно простой пример аннотации типов, давайте рассмотрим более сложный вариант.
from typing import List, Dict
prices: List[float] = [123.4, 56.7, 89.0]
user_balances: Dict[str, float] = {"Alice": 1000.0, "Bob": 750.5}
- List[float] вместо list — защита от случайных вставок строк
Tuple и Union для сложных структур
from typing import Tuple, Union
def get_price(symbol: str) -> Union[float, None]:
# вернёт цену или None, если не найдено
- Tuple[int, str] для жёсткого набора типов
- Union[X, Y] когда возможно несколько вариантов
TypedDict для диктов с фиксированным набором полей
from typing import TypedDict
class Order(TypedDict):
id: int
symbol: str
amount: float
order: Order = {"id": 1, "symbol": "BTCUSDT", "amount": 0.5}
- Помогает инструментам подсказывать поля и их типы. На самом деле вместо TypedDict можно использовать dataclass, рассказал как вот в этом посте.
Generic-функции и Protocols (продвинутый уровень)
from typing import TypeVar, Iterable, List
T = TypeVar("T")
def to_list(iterable: Iterable[T]) -> List[T]:
return list(iterable)
- TypeVar делает функцию универсальной для любых типов.
Выводы:
- Аннотации приводят к более понятному и безопасному коду.
- Инструменты проверки (mypy) ловят ошибки раньше, чем ваш код упадёт.
- В командной работе это просто must-have!
Перешлите коллегам, пусть тоже прокачают свой Python-код аннотациями типов и вы перестанете гадать что же возвращает функция 😉
#python #typing #mypy #codehacks #cleanCode
Please open Telegram to view this post
VIEW IN TELEGRAM
Telegram
DataДжунгли🌳
#PyTrickMonday
С началом новой недели друзья! Самое время разобрать парочку полезных #python трюков 🐍
1️⃣Универсальная распаковка (* и **)
Как по-красоте «разворачивать» коллекции и аргументы? Уверен вы уже видели эти загадочные звезды в коде
def connect(host…
С началом новой недели друзья! Самое время разобрать парочку полезных #python трюков 🐍
1️⃣Универсальная распаковка (* и **)
Как по-красоте «разворачивать» коллекции и аргументы? Уверен вы уже видели эти загадочные звезды в коде
def connect(host…
👍7
#SQLWednesday
Привет, друзья!
Сегодня разбираем сводные таблицы (Pivot) в SQL - как же превратить строки в колонки и сгруппировать данные по нужным критериям? Уверен у вас возникали такие желания а также это вопрос с собеседований.
Предположим дана вот такая таблица MonthlySales.
Задача: вывести по каждому product_id столбцы с месяцами 6, 7 и 8 и их суммарными amount.
С помощью #pandas это легко там есть метод .pivot() но как быть в SQL?
Подход 1. CASE … SUM() + GROUP BY (универсальный SQL)
результат:
Подход 2. PIVOT (например, в SQL Server)
Результат будет таким же, как и в первом примере.
✨ Когда использовать Pivot?
• Когда нужно быстро свернуть периодические метрики в колонки
• Для отчётов с динамическими заголовками (месяцы, категории, регионы)
• Вместо множества JOIN-ов и дополнительных CTE
Сохраняйте в избранное, пробуйте оба подхода и делитесь с друзьями и коллегами 😉
#DataEngineer #SQL #Pivot #DataJungle
Привет, друзья!
Сегодня разбираем сводные таблицы (Pivot) в SQL - как же превратить строки в колонки и сгруппировать данные по нужным критериям? Уверен у вас возникали такие желания а также это вопрос с собеседований.
Предположим дана вот такая таблица MonthlySales.
|------|-------|------------|--------|
| 2024 | 6 | P1 | 1000 |
| 2024 | 6 | P2 | 1500 |
| 2024 | 7 | P1 | 1200 |
| 2024 | 7 | P2 | 1300 |
| 2024 | 8 | P1 | 1100 |
| 2024 | 8 | P2 | 1400 |
Задача: вывести по каждому product_id столбцы с месяцами 6, 7 и 8 и их суммарными amount.
С помощью #pandas это легко там есть метод .pivot() но как быть в SQL?
Подход 1. CASE … SUM() + GROUP BY (универсальный SQL)
SELECT
product_id,
SUM(CASE WHEN month = 6 THEN amount ELSE 0 END) AS month_06,
SUM(CASE WHEN month = 7 THEN amount ELSE 0 END) AS month_07,
SUM(CASE WHEN month = 8 THEN amount ELSE 0 END) AS month_08
FROM MonthlySales
WHERE year = 2024
GROUP BY product_id
ORDER BY product_id;
результат:
| product_id | month_06 | month_07 | month_08 |
|------------|----------|----------|----------|
| P1 | 1000 | 1200 | 1100 |
| P2 | 1500 | 1300 | 1400 |
Подход 2. PIVOT (например, в SQL Server)
SELECT
product_id,
[6] AS month_06,
[7] AS month_07,
[8] AS month_08
FROM (
SELECT month, product_id, amount
FROM MonthlySales
WHERE year = 2024
) AS src
PIVOT (
SUM(amount) FOR month IN ([6], [7], [8])
) AS pvt
ORDER BY product_id;
Результат будет таким же, как и в первом примере.
✨ Когда использовать Pivot?
• Когда нужно быстро свернуть периодические метрики в колонки
• Для отчётов с динамическими заголовками (месяцы, категории, регионы)
• Вместо множества JOIN-ов и дополнительных CTE
Сохраняйте в избранное, пробуйте оба подхода и делитесь с друзьями и коллегами 😉
#DataEngineer #SQL #Pivot #DataJungle
🔥9
#SQLWednesday
Привет, data-мастера!
Сегодня разбираемся с дубликатами!
Задача классическая: оставить самую свежую запись, всё остальное - удалить. Классика #SQLinterview
Таблица Orders (да, снова она):
Нам нужно, чтобы в базе остались только самые свежие записи по каждому order_no (то есть строки 3 и 5).
Вариант 1 CTE + ROW_NUMBER() + DELETE
- CTE ranked нумерует строки внутри каждой группы order_no, начиная с самой новой (ORDER BY updated_at DESC).
- Во внешнем DELETE улетают все, у кого rn > 1 - то есть не новые.
Вариант 2 MERGE
Советы из опыта 🙂
- Бэкапни таблицу или работай в транзакции.
- Создай уникальный индекс (order_no, updated_at DESC) - это ускорит поиск свежачка и защитит от будущих дубликатов.
- Если таблица жирная, гони DELETE пачками (LIMIT 10 000) + WHERE updated_at < NOW() - INTERVAL '1 day' - чтобы не блокировать прод.
- Для truly-big-data часто лучше создать новую чистую таблицу через INSERT … SELECT и потом ALTER TABLE RENAME. Меньше блокировок.
Сохраняйте, применяйте, делитесь со своими отчаявшимися коллегами-дублерами 🙂
#DataEngineer #SQL #Dedup #DataJungle #NoDuplicates
Привет, data-мастера!
Сегодня разбираемся с дубликатами!
Задача классическая: оставить самую свежую запись, всё остальное - удалить. Классика #SQLinterview
Таблица Orders (да, снова она):
| id | order_no | status | updated_at |
|----|----------|---------|---------------------|
| 1 | A-100 | new | 2025-07-20 10:15:00 |
| 2 | A-100 | paid | 2025-07-20 11:30:00 |
| 3 | A-100 | shipped | 2025-07-21 09:05:00 |
| 4 | B-200 | new | 2025-07-19 08:40:00 |
| 5 | B-200 | paid | 2025-07-20 14:55:00 |
Нам нужно, чтобы в базе остались только самые свежие записи по каждому order_no (то есть строки 3 и 5).
Вариант 1 CTE + ROW_NUMBER() + DELETE
WITH ranked AS (
SELECT
id,
order_no,
updated_at,
ROW_NUMBER() OVER (
PARTITION BY order_no
ORDER BY updated_at DESC
) AS rn
FROM Orders
)
DELETE FROM ranked
WHERE rn > 1; -- всё, что не первое удаляем
- CTE ranked нумерует строки внутри каждой группы order_no, начиная с самой новой (ORDER BY updated_at DESC).
- Во внешнем DELETE улетают все, у кого rn > 1 - то есть не новые.
Вариант 2 MERGE
MERGE INTO Orders AS target
USING (
SELECT order_no, MAX(updated_at) AS max_ts
FROM Orders
GROUP BY order_no
) AS source
ON target.order_no = source.order_no AND target.updated_at = source.max_ts
WHEN NOT MATCHED BY SOURCE THEN
DELETE
Советы из опыта 🙂
- Бэкапни таблицу или работай в транзакции.
- Создай уникальный индекс (order_no, updated_at DESC) - это ускорит поиск свежачка и защитит от будущих дубликатов.
- Если таблица жирная, гони DELETE пачками (LIMIT 10 000) + WHERE updated_at < NOW() - INTERVAL '1 day' - чтобы не блокировать прод.
- Для truly-big-data часто лучше создать новую чистую таблицу через INSERT … SELECT и потом ALTER TABLE RENAME. Меньше блокировок.
Сохраняйте, применяйте, делитесь со своими отчаявшимися коллегами-дублерами 🙂
#DataEngineer #SQL #Dedup #DataJungle #NoDuplicates
🔥6👍1
#SQLWednesday
Привет, корпоративные заложники зарплат!
Сегодня учимся собирать дерево сотрудников и считать совокупную зарплату всей поддеревни под выбранным боссом.
Рекурсия в SQL - это магия, которой все восхищаются на #SQLinterview
Исходные данные:
Задача: Для каждого босса вывести список всех подчиненных (любых уровней) и суммарную зарплату этой команды.
Решение. WITH RECURSIVE CTE🪄
Что здесь происходит?
1. Anchor member: выбираем ТОП-босса (boss_id IS NULL).
2. Recursive member: выбираем всех, у кого boss_id равен id из предыдущего шага.
3. В итоге в OrgTree получаем иерархию со всех уровней, сохраняя путь через path.
4. Во внешнем запросе агрегируем: считаем число человек и сумму зарплат.
Результат: в нашей компании только один топ-босс Иван
6 человек - это все кроме Ивана, а их зарплаты в сумме дают 447 000.
Личный совет по SQL:
- 🚀 Для конкретного босса: в CTE в первом селекте добавьте WHERE id = 2 (например, Ольга) вместо IS NULL.
- 🔄 Избегайте циклов: в данных проверьте, что ни у кого нет «замкнутой» ссылки boss_id → id.
- 📈 Для очень глубокой иерархии может понадобиться увеличивать параметр max_recursion_depth (SQL Server) или аналогичный.
- 💾 Если зарплаты могут меняться часто, кэшируйте результаты в промежуточной таблице, чтобы не гонять тяжёлый рекурсивный запрос при каждом просмотре.
Сохраняйте в закладки, репостьте в чат команды и показывайте как делать рекурсию
#DataEngineer #SQL #RecursiveCTE #DataJungle
Привет, корпоративные заложники зарплат!
Сегодня учимся собирать дерево сотрудников и считать совокупную зарплату всей поддеревни под выбранным боссом.
Рекурсия в SQL - это магия, которой все восхищаются на #SQLinterview
Исходные данные:
| id | name | boss_id | salary |
|----|------------|---------|---------|
| 1 | Иван | NULL | 100000 |
| 2 | Ольга | 1 | 80000 |
| 3 | Сергей | 1 | 75000 |
| 4 | Марина | 2 | 60000 |
| 5 | Дмитрий | 2 | 62000 |
| 6 | Елена | 3 | 55000 |
| 7 | Алексей | 4 | 50000 |
Задача: Для каждого босса вывести список всех подчиненных (любых уровней) и суммарную зарплату этой команды.
Решение. WITH RECURSIVE CTE
WITH RECURSIVE OrgTree AS (
-- 1) Начинаем с корня (босса)
SELECT
id,
name,
boss_id,
salary,
CAST(name AS TEXT) AS path
FROM Employees
WHERE boss_id IS NULL -- топ-менеджмент
UNION ALL
-- 2) Рекурсивно добавляем прямых подчиненных
SELECT
e.id,
e.name,
e.boss_id,
e.salary,
ot.path || ' → ' || e.name
FROM Employees e
JOIN OrgTree ot
ON e.boss_id = ot.id
)
SELECT
boss.id AS boss_id,
boss.name AS boss_name,
COUNT(sub.id) AS team_size,
SUM(sub.salary) AS team_salary
FROM OrgTree sub
JOIN Employees boss
ON sub.boss_id = boss.id
GROUP BY boss.id, boss.name
ORDER BY boss_id;
Что здесь происходит?
1. Anchor member: выбираем ТОП-босса (boss_id IS NULL).
2. Recursive member: выбираем всех, у кого boss_id равен id из предыдущего шага.
3. В итоге в OrgTree получаем иерархию со всех уровней, сохраняя путь через path.
4. Во внешнем запросе агрегируем: считаем число человек и сумму зарплат.
Результат: в нашей компании только один топ-босс Иван
| boss_id | boss_name | team_size | team_salary |
|---------|-----------|-----------|-------------|
| 1 | Иван | 6 | 447000 |
6 человек - это все кроме Ивана, а их зарплаты в сумме дают 447 000.
Личный совет по SQL:
- 🚀 Для конкретного босса: в CTE в первом селекте добавьте WHERE id = 2 (например, Ольга) вместо IS NULL.
- 🔄 Избегайте циклов: в данных проверьте, что ни у кого нет «замкнутой» ссылки boss_id → id.
- 📈 Для очень глубокой иерархии может понадобиться увеличивать параметр max_recursion_depth (SQL Server) или аналогичный.
- 💾 Если зарплаты могут меняться часто, кэшируйте результаты в промежуточной таблице, чтобы не гонять тяжёлый рекурсивный запрос при каждом просмотре.
Сохраняйте в закладки, репостьте в чат команды и показывайте как делать рекурсию
#DataEngineer #SQL #RecursiveCTE #DataJungle
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5❤2
#SQLWednesday
Привет, дата джедаи !
Сегодня на столе вопрос с собеседования
UPSERT - тот самый момент, когда база решает Вставить или обновить? Без лишних SELECT, без гонок и без двойных запросов.
Для примера:
Каждый день приходит CSV(или JSON) с курсами и новыми валютами.
Нужно:
1. Обновить name / updated_at, если code уже есть.
2. Вставить строку, если валюты ещё нет.
Подход 1. Postgres: INSERT … ON CONFLICT DO UPDATE
🔍 Что здесь происходит?
- ON CONFLICT (code) — опора на уникальный индекс (или PK) по code.
- EXCLUDED — виртуальная таблица с теми строками, которые пытались вставиться.
- Фактически если code есть, поправь name и updated_at, если нет - впиши как есть.
Результат после UPSERT’а:
Подход 2. SQL Server: честный MERGE
Лайфхаки:
1. Уникальный индекс на code обязателен - иначе конфликт не поймается, а UPSERT превратится в обычный INSERT c дубликатами.
2. Часто нужно сохранять что именно поменялось. Снимай OLD.* vs NEW.* триггером или логируй updated_at.
3. Пакетная загрузка: бросай тысячи строк сразу - один UPSERT быстрее тысячи одиночных.
4. Для чудовищных объёмов (миллионы строк) иногда быстрее:
- залить всё во временную таблицу;
- UPDATE по join;
- INSERT недостающих;
- DROP temp.
Сохраняйте и UPSERT’ьте. Пусть справочники будут свежими, а дубликаты - где-нибудь не у вас в бд🔥
#DataEngineer #SQL #Upsert #ONCONFLICT #DataJungle
Привет, дата джедаи !
Сегодня на столе вопрос с собеседования
UPSERT - тот самый момент, когда база решает Вставить или обновить? Без лишних SELECT, без гонок и без двойных запросов.
Для примера:
| code | name | updated_at |
|------|---------------|---------------------|
| USD | Доллар США | 2025-07-20 10:00:00 |
| EUR | Евро | 2025-07-20 10:00:00 |
| GBP | Фунт стерлингов| 2025-07-20 10:00:00 |
Каждый день приходит CSV(или JSON) с курсами и новыми валютами.
Нужно:
1. Обновить name / updated_at, если code уже есть.
2. Вставить строку, если валюты ещё нет.
Подход 1. Postgres: INSERT … ON CONFLICT DO UPDATE
INSERT INTO Currencies (code, name, updated_at)
VALUES
('USD', 'US Dollar', NOW()),
('JPY', 'Japanese Yen', NOW()),
('EUR', 'Euro (fixed)', NOW())
ON CONFLICT (code) DO UPDATE
SET name = EXCLUDED.name,
updated_at = EXCLUDED.updated_at;
- ON CONFLICT (code) — опора на уникальный индекс (или PK) по code.
- EXCLUDED — виртуальная таблица с теми строками, которые пытались вставиться.
- Фактически если code есть, поправь name и updated_at, если нет - впиши как есть.
Результат после UPSERT’а:
| code | name | updated_at |
|------|------------------|---------------------|
| USD | US Dollar | 2025-07-22 11:35:00 |
| EUR | Euro (fixed) | 2025-07-22 11:35:00 |
| GBP | Фунт стерлингов | 2025-07-20 10:00:00 |
| JPY | Japanese Yen | 2025-07-22 11:35:00 |
Подход 2. SQL Server: честный MERGE
MERGE INTO Currencies AS target
USING (VALUES
('USD', 'US Dollar', GETDATE()),
('JPY', 'Japanese Yen', GETDATE())
) AS src(code, name, updated_at)
ON target.code = src.code
WHEN MATCHED THEN
UPDATE SET
target.name = src.name,
target.updated_at = src.updated_at
WHEN NOT MATCHED THEN
INSERT (code, name, updated_at)
VALUES (src.code, src.name, src.updated_at);
Лайфхаки:
1. Уникальный индекс на code обязателен - иначе конфликт не поймается, а UPSERT превратится в обычный INSERT c дубликатами.
2. Часто нужно сохранять что именно поменялось. Снимай OLD.* vs NEW.* триггером или логируй updated_at.
3. Пакетная загрузка: бросай тысячи строк сразу - один UPSERT быстрее тысячи одиночных.
4. Для чудовищных объёмов (миллионы строк) иногда быстрее:
- залить всё во временную таблицу;
- UPDATE по join;
- INSERT недостающих;
- DROP temp.
Сохраняйте и UPSERT’ьте. Пусть справочники будут свежими, а дубликаты - где-нибудь не у вас в бд🔥
#DataEngineer #SQL #Upsert #ONCONFLICT #DataJungle
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4❤2
#PyTrickMonday
any() и all() — твои мини-фильтры
или как проверять условия в коллекциях без циклов и лишнего кода 🔍
Часто в коде хочется спросить у Python:
“Есть ли хоть один элемент, который подходит под условие?”
“А все ли элементы ок?”
И вот тут any() и all() — настоящие ниндзя🥷
💡 Почему это удобно:
1️⃣ Читаемость: any(...) читается как хоть один, all(...) - все.
2️⃣ Меньше кода: вместо громоздкого for/if/break - одна строка.
3️⃣ Эффективность: останавливаются при первом подходящем/неподходящем элементе.
4️⃣ Гибкость: можно проверять не только числа, но и строки, объекты, словари.
Ещё пример из жизни:
📌 Запомни:
any([]) → False (в пустом списке нет ни одного истинного элемента)
all([]) → True (математика: вакуумно-истинно 😏)
⚡ Отправь этот пост коллеге- пусть тоже перестанет писать километры циклов!
#Python #DataJungle
any() и all() — твои мини-фильтры
или как проверять условия в коллекциях без циклов и лишнего кода 🔍
Часто в коде хочется спросить у Python:
“Есть ли хоть один элемент, который подходит под условие?”
“А все ли элементы ок?”
И вот тут any() и all() — настоящие ниндзя
nums = [2, 4, 6, 8]
# Есть ли хоть одно нечётное? (any)
has_odd = any(n % 2 for n in nums) # False
# Все ли чётные? (all)
all_even = all(n % 2 == 0 for n in nums) # True
💡 Почему это удобно:
1️⃣ Читаемость: any(...) читается как хоть один, all(...) - все.
2️⃣ Меньше кода: вместо громоздкого for/if/break - одна строка.
3️⃣ Эффективность: останавливаются при первом подходящем/неподходящем элементе.
4️⃣ Гибкость: можно проверять не только числа, но и строки, объекты, словари.
Ещё пример из жизни:
emails = ["[email protected]", "", "[email protected]"]
# Проверить, что у всех сотрудников есть email
if all(emails):
print("Все указали адреса")
else:
print("Кто-то забыл email")
# Проверить, что хотя бы один email корпоративный
if any(e.endswith("@company.com") for e in emails if e):
print("Есть корпоративный адрес")
📌 Запомни:
any([]) → False (в пустом списке нет ни одного истинного элемента)
all([]) → True (математика: вакуумно-истинно 😏)
⚡ Отправь этот пост коллеге- пусть тоже перестанет писать километры циклов!
#Python #DataJungle
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4
#SQLWednesday
Привет, дата-джедаи!
Сегодня у нас SQL-дуэт 👯:
1. Склейка значений в одну строку по группам
2. Поиск пропавших дат в последовательности
Две задачи, которые на собеседованиях задают так часто, что они уже снятся разработчикам SQL 🤯
1️⃣ Склейка строк по группам
Кейс:
Есть таблица Books, где каждая строка — это Автор — Книга.
Нужно получить список книг через запятую для каждого автора.
Дано:
- Обязательно указывайте ORDER BY внутри агрегатора — иначе порядок "порвёт" случайно.
- DISTINCT внутри STRING_AGG помогает, если есть дубликаты. Ну это вы и так знаете!
2️⃣ Поиск пропавших дат - Это очень полезный запрос когда вы хотите понять какие даты у вас пропущены!
Кейс:
Есть лог продаж Sales, но не за каждый день - маркетолог странно их загружает 🙄
Нужно узнать все даты без продаж в заданном диапазоне.
Postgres (generate_series):
SQL Server (рекурсивный CTE):
🔥
- Склейка по группам (STRING_AGG) → красиво группируем значения в список.
- Поиск пропавших дат → находим пробелы в данных и спасаем аналитиков.
Отдайте эти приёмы коллеге - возможно, они прямо сейчас мажут DISTINCT перед GROUP_CONCAT и не знают про generate_series 🪄
#SQL #DataJungle #STRING_AGG #generate_series #SelfJoinWithDate
Привет, дата-джедаи!
Сегодня у нас SQL-дуэт 👯:
1. Склейка значений в одну строку по группам
2. Поиск пропавших дат в последовательности
Две задачи, которые на собеседованиях задают так часто, что они уже снятся разработчикам SQL 🤯
1️⃣ Склейка строк по группам
Кейс:
Есть таблица Books, где каждая строка — это Автор — Книга.
Нужно получить список книг через запятую для каждого автора.
Дано:
author book
Дж. Роулинг Гарри Поттер 1
Дж. Роулинг Гарри Поттер 2
Дж. Роулинг Случайная вакансия
Толкин Властелин колец
Толкин Хоббит
SELECT
author,
STRING_AGG(book, ', ' ORDER BY book) AS books
FROM Books
GROUP BY author;
- Обязательно указывайте ORDER BY внутри агрегатора — иначе порядок "порвёт" случайно.
- DISTINCT внутри STRING_AGG помогает, если есть дубликаты. Ну это вы и так знаете!
2️⃣ Поиск пропавших дат - Это очень полезный запрос когда вы хотите понять какие даты у вас пропущены!
Кейс:
Есть лог продаж Sales, но не за каждый день - маркетолог странно их загружает 🙄
Нужно узнать все даты без продаж в заданном диапазоне.
Postgres (generate_series):
-- Задаём диапазон от MIN до MAX даты
WITH all_dates AS (
SELECT generate_series(
(SELECT MIN(sale_date) FROM Sales)::date,
(SELECT MAX(sale_date) FROM Sales)::date,
'1 day'
) AS dt
)
SELECT dt
FROM all_dates a
LEFT JOIN Sales s ON s.sale_date = a.dt
WHERE s.sale_date IS NULL;
SQL Server (рекурсивный CTE):
DECLARE @start DATE = (SELECT MIN(sale_date) FROM Sales);
DECLARE @end DATE = (SELECT MAX(sale_date) FROM Sales);
WITH dates AS (
SELECT @start AS dt
UNION ALL
SELECT DATEADD(DAY, 1, dt)
FROM dates
WHERE dt < @end
)
SELECT dt
FROM dates d
LEFT JOIN Sales s ON s.sale_date = d.dt
WHERE s.sale_date IS NULL
OPTION (MAXRECURSION 0);
🔥
- Склейка по группам (STRING_AGG) → красиво группируем значения в список.
- Поиск пропавших дат → находим пробелы в данных и спасаем аналитиков.
Отдайте эти приёмы коллеге - возможно, они прямо сейчас мажут DISTINCT перед GROUP_CONCAT и не знают про generate_series 🪄
#SQL #DataJungle #STRING_AGG #generate_series #SelfJoinWithDate
🔥7❤2
Media is too big
VIEW IN TELEGRAM
Что интересного можно узнать в воскресение?
Например:
- Как делать автоматизированный канал про ИИ который ведет ИИ?🤯
- Как экономить деньги если тыгениальный программист? 😄
- Что такое n8n?❓
- Google таблица как база данных не так и плоха!💡
Дорогие подписчики только для вас эксклюзивный контент !
В этом видео я отвечу на вопросы выше, а также вы получите ссылку на мой новый интересный автоматизированный - канал 👯
Подпишитесь на новый канал @iishnoe если любите ИИ, пошарьте друзьям это видео чтобы они тоже не терялись в технологиях будущего.
Надеюсь будет интересно, лайки коменты и респекты приветствуются🔥
Например:
- Как делать автоматизированный канал про ИИ который ведет ИИ?
- Как экономить деньги если ты
- Что такое n8n?
- Google таблица как база данных не так и плоха!
Дорогие подписчики только для вас эксклюзивный контент !
В этом видео я отвечу на вопросы выше, а также вы получите ссылку на мой новый интересный автоматизированный - канал 👯
Подпишитесь на новый канал @iishnoe если любите ИИ, пошарьте друзьям это видео чтобы они тоже не терялись в технологиях будущего.
Надеюсь будет интересно, лайки коменты и респекты приветствуются
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3🔥3
Forwarded from ИИшное
🔍 Нашли золото в мире ИИ: уникальные системные промты от топовых нейронок Anthropic, Google, OpenAI, Perplexity и xAI. Эти внутренние инструкции раскрывают, что можно, а что нельзя моделям, какие инструменты используются и как они мыслят. Это настоящее сокровище для любителей ИИ:
🤖 Поскольку вы узнаете, почему искусственный интеллект иногда «зависает» или отказывает в ответе, и как можно обойти эти ограничения.
⚙️ Используйте эту информацию для локальных моделей AI.
🚀 Отличное начало, если создаете собственные нейронные сети.
#AI #MachineLearning #DeepLearning #НейронныеСети
🤖 Поскольку вы узнаете, почему искусственный интеллект иногда «зависает» или отказывает в ответе, и как можно обойти эти ограничения.
⚙️ Используйте эту информацию для локальных моделей AI.
🚀 Отличное начало, если создаете собственные нейронные сети.
#AI #MachineLearning #DeepLearning #НейронныеСети
❤2
#PyTrickMonday
Часть 1. Базовое знакомство с асинхроном.
asyncio - когда Python становится акробатом 🤸♂️
или как запускать кучу задач одновременно, не дожидаясь поочерёдно
Глупый пример но тем не менее 🙂
"тебе надо сходить за хлебом, оплатить коммуналку и приготовить кофе."
Синхронно: делаешь всё подряд, ждёшь пока закипит вода ☕, потом топаешь в магазин.
Асинхронно: ставишь чайник, пока он греется — бежишь за хлебом, по пути платишь счета.
так работает asyncio
Результат: всё завершается параллельно, а не по очереди.
• async — объявляешь асинхронную функцию;
• await — говоришь “подожди, но пока можно делать другое”;
• gather — запускает несколько задач одновременно.
📌 Когда использовать asyncio:
• ✅ Когда много I/O-операций: запросы к API, базы данных, парсинг сайтов.
• ✅ Когда важна скорость реакции: чат-боты, стриминг, веб-сервисы.
• ✅ Когда нужно уместить сотни/тысячи соединений в один процесс.
🚫 Когда НЕ использовать:
• ❌ Для тяжёлых вычислений (ML, обработка больших массивов) — тут поможет multiprocessing или Spark.
• ❌ Если у тебя простая линейная программа (асинхронщина только усложнит код).
• ❌ Когда надо жать CPU на 100% (асинхронный код от этого не ускорится).
⚡ В следующей части покажу, как управлять этими задачами: ставить таймауты, отменять и ловить «медленников».
#Python #Asyncio #DataJungle
Часть 1. Базовое знакомство с асинхроном.
asyncio - когда Python становится акробатом 🤸♂️
или как запускать кучу задач одновременно, не дожидаясь поочерёдно
Глупый пример но тем не менее 🙂
"тебе надо сходить за хлебом, оплатить коммуналку и приготовить кофе."
Синхронно: делаешь всё подряд, ждёшь пока закипит вода ☕, потом топаешь в магазин.
Асинхронно: ставишь чайник, пока он греется — бежишь за хлебом, по пути платишь счета.
так работает asyncio
import asyncio
async def task(name, delay):
await asyncio.sleep(delay)
print(f"{name} done after {delay}s")
async def main():
await asyncio.gather(
task("💡 Coffee", 3),
task("🍞 Bread", 2),
task("💸 Bills", 1),
)
asyncio.run(main())
Результат: всё завершается параллельно, а не по очереди.
• async — объявляешь асинхронную функцию;
• await — говоришь “подожди, но пока можно делать другое”;
• gather — запускает несколько задач одновременно.
📌 Когда использовать asyncio:
• ✅ Когда много I/O-операций: запросы к API, базы данных, парсинг сайтов.
• ✅ Когда важна скорость реакции: чат-боты, стриминг, веб-сервисы.
• ✅ Когда нужно уместить сотни/тысячи соединений в один процесс.
🚫 Когда НЕ использовать:
• ❌ Для тяжёлых вычислений (ML, обработка больших массивов) — тут поможет multiprocessing или Spark.
• ❌ Если у тебя простая линейная программа (асинхронщина только усложнит код).
• ❌ Когда надо жать CPU на 100% (асинхронный код от этого не ускорится).
⚡ В следующей части покажу, как управлять этими задачами: ставить таймауты, отменять и ловить «медленников».
#Python #Asyncio #DataJungle
❤2😱2
#SQLWednesday
Привет, SQL-джедаи ⚔️
Сегодня разберём задачу SQL интервью, без которой ни один аналитик не уходит со свидания с данными: сравнение с прошлым периодом.
Хотим понять: выросли мы или упали, и насколько сильно 📉📈.
Есть таблица Sales (дата, сумма продаж).
Нужно:
1. Посчитать разницу с предыдущим днём.
2. Узнать % прироста/падения.
Что тут за магия:
• LAG() — смотрим «назад» на 1 строку.
• diff — простое вычитание.
• pct_change — разница в процентах (NULLIF спасает от деления на 0).
🔥 А если нужен рост/падение к прошлому месяцу - то же самое, только PARTITION BY EXTRACT(MONTH FROM sale_date) или DATE_TRUNC('month', sale_date).
⚡ Это хорошая задачка на собесах, потому что проверяет: умеешь ли ты в оконнынные функции или продолжаешь self-joinить😏
#SQL #WindowFunctions #DataJungle
Привет, SQL-джедаи ⚔️
Сегодня разберём задачу SQL интервью, без которой ни один аналитик не уходит со свидания с данными: сравнение с прошлым периодом.
Хотим понять: выросли мы или упали, и насколько сильно 📉📈.
Есть таблица Sales (дата, сумма продаж).
Нужно:
1. Посчитать разницу с предыдущим днём.
2. Узнать % прироста/падения.
SELECT
sale_date,
amount,
LAG(amount) OVER (ORDER BY sale_date) AS prev_amount,
amount - LAG(amount) OVER (ORDER BY sale_date) AS diff,
ROUND(
100.0 * (amount - LAG(amount) OVER (ORDER BY sale_date))
/ NULLIF(LAG(amount) OVER (ORDER BY sale_date), 0),
2
) AS pct_change
FROM Sales;
Что тут за магия:
• LAG() — смотрим «назад» на 1 строку.
• diff — простое вычитание.
• pct_change — разница в процентах (NULLIF спасает от деления на 0).
⚡ Это хорошая задачка на собесах, потому что проверяет: умеешь ли ты в оконнынные функции или продолжаешь self-joinить
#SQL #WindowFunctions #DataJungle
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4🔥4🤯1
#PyTrickMonday
asyncio — Часть 2
Контроль задач: таймауты, отмена, обработка «медленников» 🎪
Когда задач много, «запустить и забыть» уже не работает. Нужен контроль: кто отвалился, кто завис, кого пора отменить.
1) gather vs wait vs as_completed
asyncio.gather(*tasks) — простейший «всё сразу».
Удобно, когда:
• все задачи равнозначны;
• результат нужен только когда всё готово.
• Плюс: может собирать результаты в порядке аргументов.
• Минус: по умолчанию ошибка одной задачи «роняет» всё (см. return_exceptions=True).
asyncio.wait(tasks, return_when=...) — когда нужен статус-контроль:
• FIRST_COMPLETED — продолжай, как только что-то завершилось;
• FIRST_EXCEPTION — реагируй на первую ошибку;
• ALL_COMPLETED — эквивалент дождаться всех, но с наборами done/pending.
• asyncio.as_completed(tasks) — стримишь результаты по мере готовности:
• обрабатываешь «быстрые» ответы, не дожидаясь «черепах».
2) Таймауты: жёсткий дедлайн для всего или для каждой задачи
Глобальный таймаут (для группы)
- Индивидуальные таймауты (каждой задаче свой лимит)
3) Отмена задач: .cancel() и обработка CancelledError
Задачи нужно уметь «выключать культурно», освобождая ресурсы.
4) Обрабатывать результаты «как приходят» — as_completed
Отлично подходит для «медленного зоопарка» API.
5) Частые грабли и лайфхаки
• gather(..., return_exceptions=True) — не роняй весь батч из-за одной ошибки; обработай её в результате.
• Создавай задачи через asyncio.create_task(), если нужно запустить и продолжать работу (иначе просто await подряд).
• Не блокируй event loop: никакого time.sleep() и долгих CPU-циклов внутри async — только await I/O или выноси CPU в concurrent.futures/multiprocessing.
• Таймауты — не панацея: всегда закрывай ресурсы и корректно обрабатывай отмену.
• Смешивать threading и asyncio можно, но осторожно: для блокирующих библиотек используй loop.run_in_executor(...).
⚡ Контролируй асинхронность - и она начнёт экономить тебе время, а не нервы 😏 делитесь этим с друзьями и коллегами !
#Python #Asyncio #DataJungle
asyncio — Часть 2
Контроль задач: таймауты, отмена, обработка «медленников» 🎪
Когда задач много, «запустить и забыть» уже не работает. Нужен контроль: кто отвалился, кто завис, кого пора отменить.
1) gather vs wait vs as_completed
asyncio.gather(*tasks) — простейший «всё сразу».
Удобно, когда:
• все задачи равнозначны;
• результат нужен только когда всё готово.
• Плюс: может собирать результаты в порядке аргументов.
• Минус: по умолчанию ошибка одной задачи «роняет» всё (см. return_exceptions=True).
asyncio.wait(tasks, return_when=...) — когда нужен статус-контроль:
• FIRST_COMPLETED — продолжай, как только что-то завершилось;
• FIRST_EXCEPTION — реагируй на первую ошибку;
• ALL_COMPLETED — эквивалент дождаться всех, но с наборами done/pending.
• asyncio.as_completed(tasks) — стримишь результаты по мере готовности:
• обрабатываешь «быстрые» ответы, не дожидаясь «черепах».
2) Таймауты: жёсткий дедлайн для всего или для каждой задачи
Глобальный таймаут (для группы)
import asyncio
async def api_call(t): # имитируем сетевой вызов
await asyncio.sleep(t)
return f"ok in {t}s"
async def main():
tasks = [api_call(1), api_call(5), api_call(10)]
try:
# «Всё должно уложиться за 6 секунд»
res = await asyncio.wait_for(asyncio.gather(*tasks), timeout=6)
print(res)
except asyncio.TimeoutError:
print("⏰ Глобальный таймаут: не все успели!")
asyncio.run(main())
- Индивидуальные таймауты (каждой задаче свой лимит)
import asyncio
async def fetch(t):
await asyncio.sleep(t)
return t
async def safe_fetch(t, limit=2):
try:
return await asyncio.wait_for(fetch(t), timeout=limit)
except asyncio.TimeoutError:
return f"timeout>{limit}s"
async def main():
tasks = [safe_fetch(1), safe_fetch(3), safe_fetch(0.5)]
print(await asyncio.gather(*tasks))
asyncio.run(main())
3) Отмена задач: .cancel() и обработка CancelledError
Задачи нужно уметь «выключать культурно», освобождая ресурсы.
import asyncio
async def worker():
try:
while True:
await asyncio.sleep(0.5) # работа
except asyncio.CancelledError:
print("🔌 Получил cancel, закрываю соединения...")
# тут — закрытие сессий/курсов/файлов
raise # пробрасываем дальше (правильно!)
async def main():
task = asyncio.create_task(worker())
await asyncio.sleep(2) # пусть поработает
task.cancel()
try:
await task
except asyncio.CancelledError:
print("✅ Отмена завершена корректно")
asyncio.run(main())
4) Обрабатывать результаты «как приходят» — as_completed
Отлично подходит для «медленного зоопарка» API.
import asyncio
from random import randint
async def call(i):
t = randint(1, 5)
await asyncio.sleep(t)
return f"#{i} in {t}s"
async def main():
tasks = [asyncio.create_task(call(i)) for i in range(5)]
for fut in asyncio.as_completed(tasks):
try:
res = await fut
print("✅", res)
except Exception as e:
print("❌ ошибка:", e)
asyncio.run(main())
5) Частые грабли и лайфхаки
• gather(..., return_exceptions=True) — не роняй весь батч из-за одной ошибки; обработай её в результате.
• Создавай задачи через asyncio.create_task(), если нужно запустить и продолжать работу (иначе просто await подряд).
• Не блокируй event loop: никакого time.sleep() и долгих CPU-циклов внутри async — только await I/O или выноси CPU в concurrent.futures/multiprocessing.
• Таймауты — не панацея: всегда закрывай ресурсы и корректно обрабатывай отмену.
• Смешивать threading и asyncio можно, но осторожно: для блокирующих библиотек используй loop.run_in_executor(...).
⚡ Контролируй асинхронность - и она начнёт экономить тебе время, а не нервы 😏 делитесь этим с друзьями и коллегами !
#Python #Asyncio #DataJungle
👍3❤2
Поиск отличий между таблицами («дифф»)
#SQLWednesday
👯♂️ Две таблицы заходят в бар… и нужно понять, чем они отличаются.
Классика собесов - сравнить два снапшота данных.
Есть таблицы:
• Customers_2024
• Customers_2025
Нужно:
1. Кто появился новым?
2. Кто удалился?
3. У кого изменились данные?
Что тут важно:
• JOIN-ы вместо минусов - так быстрее и прозрачнее.
• Для сравнения всех полей можно собрать CHECKSUM(*) или HASHBYTES (SQL Server) / md5(row::text) (Postgres).
• Не забудьте про NULL: <> не работает, используйте IS DISTINCT FROM (Postgres) или EXCEPT для простого диффа.
Конечно все это уже давно решается с помощью SCD но это задача с собеседований на DE не редкость.
🔥 Такой «diff» помогает искать не только ошибки загрузки, но и баги в ETL, поэтому это валидно и для анализа и для DE.
#SQL #DataDiff #ETL #DataJungle
#SQLWednesday
👯♂️ Две таблицы заходят в бар… и нужно понять, чем они отличаются.
Классика собесов - сравнить два снапшота данных.
Есть таблицы:
• Customers_2024
• Customers_2025
Нужно:
1. Кто появился новым?
2. Кто удалился?
3. У кого изменились данные?
-- Новые клиенты (есть в 2025, нет в 2024)
SELECT c2025.id, c2025.name
FROM Customers_2025 c2025
LEFT JOIN Customers_2024 c2024 ON c2024.id = c2025.id
WHERE c2024.id IS NULL;
-- Удалённые клиенты (были, но пропали)
SELECT c2024.id, c2024.name
FROM Customers_2024 c2024
LEFT JOIN Customers_2025 c2025 ON c2025.id = c2024.id
WHERE c2025.id IS NULL;
-- Изменения в данных (id совпал, поля разные)
SELECT c2024.id,
c2024.name AS old_name,
c2025.name AS new_name
FROM Customers_2024 c2024
JOIN Customers_2025 c2025 ON c2025.id = c2024.id
WHERE c2024.name <> c2025.name; -- вот тут будет ошибка если NULL поэтому будьте аккуратны
Что тут важно:
• JOIN-ы вместо минусов - так быстрее и прозрачнее.
• Для сравнения всех полей можно собрать CHECKSUM(*) или HASHBYTES (SQL Server) / md5(row::text) (Postgres).
• Не забудьте про NULL: <> не работает, используйте IS DISTINCT FROM (Postgres) или EXCEPT для простого диффа.
Конечно все это уже давно решается с помощью SCD но это задача с собеседований на DE не редкость.
🔥 Такой «diff» помогает искать не только ошибки загрузки, но и баги в ETL, поэтому это валидно и для анализа и для DE.
#SQL #DataDiff #ETL #DataJungle
🔥7👍1