SQL и Анализ данных
12.6K subscribers
679 photos
66 videos
3 files
693 links
Базы данных и всё, что с ними связано!

Сотрудничество: @haarrp

РКН № 6766085482
Download Telegram
💻 Резервное копирование PostgreSQL по-взрослому

В статье рассказывается о том, как грамотно организовать резервное копирование PostgreSQL с помощью стандартных инструментов командной строки.

Автор подробно сравнивает форматы дампов (plain, custom, tar, directory), объясняет, в каких сценариях лучше использовать каждый из них, и показывает замеры по времени, объёму и возможностям восстановления.

Читать статью

tags: #статья

SQL Community | Чат
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥52
Вопрос с собеседования

Почему в WHERE нельзя использовать алиасы из SELECT?

Ответ: Потому что SQL-запрос выполняется не в том порядке, в котором написан. На этапе выполнения WHERE часть SELECT ещё не обработана, а значит, её алиасы (псевдонимы) не существуют. Алиасы определяются только на этапе SELECT, который идёт после WHERE.

tags: #собеседование

SQL Community | Чат
Please open Telegram to view this post
VIEW IN TELEGRAM
🤯10👍95
💻 Путеводитель по базам данных

Делимся книгой для тех, кто хочет разобраться, как устроены современные СУБД на уровне архитектуры, алгоритмов и структур данных.

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

Скачать в PDF

tags: #полезное

SQL Community | Чат
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥10👍41
Please open Telegram to view this post
VIEW IN TELEGRAM
😁37🔥4
Forwarded from Data Science. SQL hub
Media is too big
VIEW IN TELEGRAM
📜 История SQL — от лабораторной идеи до «языка данных» № 1

Как появился самый известный язык работы с базами, почему он едва не остался «Сиквелом» и какие любопытные факты о нём редко всплывают в учебниках.

1. Всё началось с таблицы на бумаге

- 1970 г. — британский математик Эдгар Ф. Кодд публикует культовую статью *“A Relational Model of Data for Large Shared Data Banks”*.
- В ней впервые прозвучала идея: хранить данные в виде связанных таблиц, а не как запутанные иерархии (IMS) или сетевые графы (Codasyl).
- Коллеги в IBM скептически называли это «бумагой на буквы», но разрешили сделать прототип, чтобы проверить утопию Кодда на практике.

2. SEQUEL — «английский» запрос к таблицам

- 1973–1974 гг. — в лаборатории IBM San José (ныне Almaden) двое молодых исследователей, Дональд Чемберлин и Рэймонд Бойс, берутся за проект System R.
- Чтобы обращаться к реляционным таблицам, они придумывают Structured English QUEry Language — SEQUEL.
- Ключевая фишка — запросы выглядят почти как английские предложения:

SELECT name, salary
FROM employees
WHERE dept = 'R&D';

- В 1974‑м публикуют первую спецификацию; академики критикуют за «слишком поверхностный английский», но программисты в восторге.

3. Почему SEQUEL стал SQL

- Торговая марка “SEQUEL” уже принадлежала авиастроительной компании *Hawker Siddeley*.
- IBM, опасаясь суда, в 1976 г. официально отказывается от «E» и оставляет SQL (Structured Query Language).
- *Небольшая путаница осталась навсегда: кто‑то произносит «эс‑кью‑эл», кто‑то — «сиквел».*

4. Коммерческий взлёт


- 1978 | Первая демонстрация System R внутри IBM | показала, что SQL работает быстрее ожиданий |
- 1979 | Стартап Relational Software (позже Oracle**) выпускает **Oracle V2 — первый коммерческий SQL‑движок | IBM ещё не успела выйти на рынок
- 1981 | IBM выпускает SQL/DS для мейнфреймов | стандарт де‑факто закрепляется
- 1983 | Дебют DB2 — теперь SQL есть почти в каждом крупном банке

5. Стандартизация и эволюция

- ANSI SQL‑86SQL‑92 (появился `JOIN ... ON`) → SQL:1999 (рекурсия, триггеры) → SQL:2003 (XML) → … → SQL:2023 (JSON, property graphs).
- Каждые 3–5 лет комитет добавляет «модные» возможности, но 90 % повседневных запросов всё ещё укладываются в синтаксис 1980‑х.

6. Забавные факты, которые украсят small talk 🍸

1. NULL ≠ 0 и NULL ≠ NULL — «неизвестное значение» нарушает законы логики, за что его зовут *“пятой ногой”* реляционной алгебры.
2. `SELECT *` — наследие печати на станке. Звёздочка означала «все колонки», чтобы не писать их руками в 132‑символьных перфокартах.
3. Команда GO в MS SQL Server не принадлежит стандарту SQL — это директива из старого клиента isql.
4. В Oracle долго не было LIMIT, а в MySQL —QL — от лабора Поэтому админы шутили: «истинный межплатформенный SQL — это `SELECT 1;`».
5. Первый SQL‑вирус — червь *Slammer* (2003) — парализовал интернет за 10 минут через уязвимость в SQL Server 2000.
6. SQL — декларативный язык, но внутри СУБД каждый SELECT превращается в процедурный план.
7.Ф. Кодд публикуетпридумали позже, чемлабораторн Сначала удалять целую БД казалось слишком опасным.

7. Почему SQL живёт дольше модных NoSQL‑наследников

- Математическая база. Таблицы + операции Кодда образуют алгебру с предсказуемой оптимизацией.
- Стандарты и переносимость. Код двадцатилетней давности можно запустить в современной Postgres или MariaDB.
- Большая экосистема. От Excel‑плагинов до BigQuery — везде так или иначе поддерживается SQL‑диалект.
- Сопротивляемость моде. Каждый «убийца SQL» (MapReduce, GraphQL, документные БД) в итоге добавляет свой адаптер SELECT ….

Итог: SQL родился как эксперимент IBM, пережил смену названий и юридические баталии, но в итоге стал «лентой Мёбиуса» мира данных: можно зайти с любой стороны — и всё равно окажешься в FROM.

https://www.youtube.com/shorts/EuFjzuVHkHE

@sqlhub -подписаться
👍23🔥114🤨4🥰1💊1
Друзья, мы создали канал с книгами по SQL и залили туда наверное самую большую подборку книг по SQL. Около 200 книг. Каждую неделю выходят еще новые книги.

Подпишитесь, там будут книги и марафоны задач по SQL и много редкой литературы. https://t.me/sql_lib
👍72🔥2💊2🤨1
🛢️ SQL-задача с подвохом: NULL ловушка

Условие:

Есть таблица employees:

| id | name | department |
|-----|----------|------------|
| 1 | Alice | Sales |
| 2 | Bob | NULL |
| 3 | Charlie | HR |
| 4 | Diana | NULL |
| 5 | Eve | Sales |

Ты хочешь выбрать всех сотрудников, которые не работают в отделе Sales. Пишешь простой запрос:


SELECT * FROM employees
WHERE department <> 'Sales';


Вопрос:
Какие строки вернёт этот запрос? Почему результат может удивить даже опытных специалистов?

---

🔍 Разбор:

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

- Bob (NULL)
- Charlie (HR)
- Diana (NULL)

Но вот главный подвох: NULL — это "неизвестное значение", и в SQL любые сравнения с NULL дают UNKNOWN.

Запрос:


WHERE department <> 'Sales'


- Charlie (HR): вернётся, потому что HR <> Sales.
- Bob (NULL): НЕ вернётся, потому что NULL <> 'Sales' даёт UNKNOWN (не TRUE).
- Diana (NULL): по той же причине.
- Alice и Eve: потому что у них Sales.

---

Фактический результат:

| id | name | department |
|-----|---------|------------|
| 3 | Charlie | HR |

---

💥 Подвох:

Многие думают, что NULL автоматически участвует в сравнении как будто это "значение", но SQL строго следует трёхзначной логике:

- TRUE
- FALSE
- UNKNOWN

В WHERE фильтре остаются только строки, где условие = TRUE. Строки с NULL дают UNKNOWN и отбрасываются.

---

🛠 Как исправить запрос, чтобы включить сотрудников без отдела (NULL):


SELECT * FROM employees
WHERE department <> 'Sales' OR department IS NULL;


Теперь вернётся:

| id | name | department |
|-----|---------|------------|
| 2 | Bob | NULL |
| 3 | Charlie | HR |
| 4 | Diana | NULL |

---

Вывод:

• В SQL сравнения с NULL всегда возвращают UNKNOWN.
• Обычные условия (`<>`, =, >, <`) **не учитывают NULL правильно**, если явно не проверить `IS NULL или IS NOT NULL.
• Даже простой фильтр может дать неожиданный результат, если в данных есть пропуски.

💡 Бонус-вопрос:
Что будет, если использовать NOT department = 'Sales' вместо department <> 'Sales'? 😉

SQL Community |
Please open Telegram to view this post
VIEW IN TELEGRAM
👍25🔥126🤨2
Forwarded from Machinelearning
🔥 9 бесплатных курсов c HuggingFace по искусственному интеллекту!

➡️Узнайте, как обучать, настраивать и развертывать большие языковые модели с помощью HuggingFace Transformers.
https://huggingface.co/learn/llm-course/chapter1/1

➡️Курс по AI-агентам
Создавайте инструменты с многоэтапным мышлением, используя LangChain и HF.
https://huggingface.co/learn/agents-course/unit0/introduction

➡️ Курс по глубокому обучению с подкреплением (Deep RL)
Научите агентов принимать решения и учиться на основе окружающей среды.
https://huggingface.co/learn/deep-rl-course/unit0/introduction

➡️ Курс по компьютерному зрению
Изучите как работает OCR, сегментация и классификация изображений с моделями HuggingFace.
https://huggingface.co/learn/audio-course/chapter0/introduction

➡️ Курс по работе с аудио
Применяйте трансформеры к аудио: распознавание речи, тегирование музыки и синтез речи.
https://huggingface.co/learn/audio-course/chapter0/introduction

➡️ Курс по машинному обучению для игр
Узнайте, как ИИ меняет разработку игр: от поведения NPC до генерации контента.
https://huggingface.co/learn/ml-games-course/unit0/introduction

➡️ Курс по машинному обучению для 3D
Работайте с 3D-данными, такими как облака точек и сетки, на стыке графики и ML.
https://huggingface.co/learn/ml-for-3d-course/unit0/introduction

➡️ Курс по диффузионным моделям
Погрузитесь в технологию, лежащую в основе DALL·E и Stable Diffusion, и научитесь генерировать изображения.
https://huggingface.co/learn/diffusion-course/unit0/1

➡️ Кулинарная книга по открытому ИИ (Open-Source AI Cookbook)
Коллекция практических ноутбуков от реальных разработчиков ИИ — учитесь, копируйте код и создавайте свои проекты. https://huggingface.co/learn/cookbook/index

@ai_machinelearning_big_data - подписаться

#free #courses #opensource #huggingface
Please open Telegram to view this post
VIEW IN TELEGRAM
7🔥3👍2💊2🤨1
🧠 SQL-задача с подвохом: “Найди самого активного… по количеству разных друзей”

📘 Условие

У тебя есть таблица дружбы:


friends(user_id, friend_id)


Здесь каждая строка означает, что user_id дружит с friend_id.
Записи всегда односторонние: если есть (1, 2), это не значит, что будет (2, 1).

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

Пример попытки:


SELECT user_id, COUNT(friend_id) AS total_friends
FROM friends
GROUP BY user_id
ORDER BY total_friends DESC
LIMIT 1;


🔍 Вопрос:

1) В чём здесь может быть логическая ошибка?
2) Какую строку подсчитает COUNT(friend_id)?
3) Когда нужно использовать COUNT(DISTINCT friend_id)?
4) Как обойти случай, если один и тот же друг записан несколько раз?

Разбор подвоха

💣 Проблема: один пользователь может быть записан как друг **несколько раз**, особенно если приложение допускает дубли (или "перезапросы дружбы").

Пример:

```sql
INSERT INTO friends VALUES (1, 2), (1, 2), (1, 3);
```

В этом случае:

```sql
SELECT COUNT(friend_id) FROM friends WHERE user_id = 1;
-- → вернёт 3
```

Но реальных друзей у пользователя `1` — только **2**: `2` и `3`.

Решение:

Используй `COUNT(DISTINCT friend_id)`:

```sql
SELECT user_id, COUNT(DISTINCT friend_id) AS unique_friends
FROM friends
GROUP BY user_id
ORDER BY unique_friends DESC
LIMIT 1;
```

🎯 Дополнительно можно убрать самого пользователя из списка друзей (на случай ошибок):

```sql
WHERE user_id != friend_id
```

⚠️ Подвох

• `COUNT()` без `DISTINCT` ловит даже опытных — особенно если в БД возможны дубли
• `LIMIT 1` не гарантирует "уникального победителя", если у нескольких одинаковый счёт
• Иногда friendship бывает и симметричной, тогда нужна защита от двойного счёта
👀11👍92🔥2💊1
💻 Beekeeper Studio — современный и удобный open-source SQL-клиент для MySQL, Postgres, SQLite, SQL Server и не только

git clone git@github.com:<your-username>/beekeeper-studio.git beekeeper-studio
cd beekeeper-studio/
yarn install

yarn run electron:serve


Beekeeper Studio без проблем работает на всех платформах: Linux, MacOS и Windows

🖥 GitHub

@sqlhub
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
8👍3🔥2🤨2
🧠 SQL-задача с подвохом (Oracle)

Тема: агрегаты, подзапросы, фильтрация без оконных функци

📌 Задача:

Дана таблица EMPLOYEES:


CREATE TABLE EMPLOYEES (
ID NUMBER,
DEPT VARCHAR2(50),
SALARY NUMBER
);


Пример данных:

| ID | DEPT | SALARY |
|----|----------|--------|
| 1 | HR | 4000 |
| 2 | HR | 3000 |
| 3 | IT | 5000 |
| 4 | IT | 5000 |
| 5 | Finance | 6000 |
| 6 | Finance | 5500 |


🧩 Найти:
Выведите отделы, где ЗП всех сотрудников одинаковая, и эту ЗП.

🎯 Подвох:
- многие начинают с GROUP BY DEPT, но обычный AVG или SUM не помогут
- нужно найти отделы, где MIN(SALARY) = MAX(SALARY), но не забыть, что отдел может содержать одного сотрудника (это корректно)

Ожидаемый результат:

| DEPT | SALARY |
|---------|--------|
| IT | 5000 |



🔍 Решение:

```sql
SELECT DEPT, MIN(SALARY) AS SALARY
FROM EMPLOYEES
GROUP BY DEPT
HAVING MIN(SALARY) = MAX(SALARY);
```

📌 **Пояснение подвоха:**
- `HAVING MIN = MAX` — проверяет, что все значения в группе одинаковые
- Не нужно подзапросов и оконных функций — решение минималистично и эффективно
- Часто используется для поиска «однородных» групп

🧪 Попробуй модифицировать запрос:
• Вывести только те отделы, где **больше одного сотрудника** и ЗП одинаковая
• Или отделы, где все сотрудники получают **разную** ЗП

👍 Лайк, если хотите задачу сложнее

SQL Community
Please open Telegram to view this post
VIEW IN TELEGRAM
28👍15🔥5🤨2
🧠 SQL-задача: "Самая длинная непрерывная сессия"

Условие:
Есть таблица логинов пользователей:


CREATE TABLE user_logins (
user_id INT,
login_time TIMESTAMP
);


Нужно — для каждого пользователя найти его максимальную по длине цепочку непрерывных ежедневных входов.
Если он заходил 1, 2, 3, 5 и 6 января — самая длинная последовательность без пропусков: 3 дня (1–3 января).

Учитываются только уникальные дни входа (то есть даже если зашёл 3 раза в день — это один день).

Ожидаемый результат:

| user_id | start_date | end_date | chain_length |
|---------|------------|------------|--------------|
| 101 | 2024-01-01 | 2024-01-03 | 3 |
| 102 | 2024-03-05 | 2024-03-09 | 5 |


Решение основано на классической технике: «группировка по смещению».

Шаги:

1. Убираем дубликаты по датам
2. Пронумеровываем строки по `ROW_NUMBER` для каждой даты входа
3. Вычисляем разницу между датой и порядковым номером — она будет **одинакова для цепочки подряд идущих дат**
4. Группируем по этой разности и ищем максимальную длину цепи

Решение (PostgreSQL):

```sql
WITH distinct_dates AS (
SELECT DISTINCT user_id, DATE(login_time) AS login_date
FROM user_logins
),
numbered AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY login_date) AS rn
FROM distinct_dates
),
grouped AS (
SELECT *,
login_date - INTERVAL '1 day' * rn AS grp
FROM numbered
),
grouped_ranges AS (
SELECT user_id,
MIN(login_date) AS start_date,
MAX(login_date) AS end_date,
COUNT(*) AS chain_length
FROM grouped
GROUP BY user_id, grp
)
SELECT *
FROM (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY chain_length DESC) AS rnk
FROM grouped_ranges
) final
WHERE rnk = 1;
```

Как работает:

- `ROW_NUMBER` генерирует позицию в порядке дат
- Вычитание `login_date - rn` создаёт искусственный "ключ", по которому мы группируем
- Внутри группы — последовательные даты
- Выбираем цепочку с максимальной длиной по каждому `user_id`

Почему это задача с подвохом:

• Нельзя решить через простой GROUP BY
• Требуется умение работать с датами, оконными функциями и многоступенчатыми CTE
• Это реальный кейс из аналитики поведения пользователей, встречается в проде

SQL Community
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥15👍75🤨2💊2
🧠 8 полезных советов по Oracle SQL

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

1. Используй `PIVOT`/`UNPIVOT` вместо `CASE`
Поворот таблиц через PIVOT делает код чище и легче масштабируется.


SELECT * FROM (
SELECT department_id, gender, salary FROM employees
)
PIVOT (
SUM(salary) FOR gender IN ('M' AS male_salary, 'F' AS female_salary)
);


🧱 2. Вызывай функции как таблицы (`TABLE()` + PL/SQL)
Если функция возвращает коллекцию, её можно SELECT’ить напрямую:


SELECT * FROM TABLE(my_package.get_active_users(SYSDATE));


🚀 3. Генерация чисел без тормозов
CONNECT BY LEVEL — это медленно. Лучше так:


SELECT rownum FROM dual CONNECT BY rownum <= 10000;


🔍 4. Помогай оптимизатору через `CARDINALITY` hint
Oracle может ошибаться в оценке количества строк. Подскажи ему:


SELECT /*+ cardinality(e 100000) */ * FROM employees e ...


🧠 5. Используй `RESULT_CACHE` для повторяемых данных
Снизь нагрузку:


SELECT /*+ RESULT_CACHE */ * FROM country_codes;


📉 6. Никогда не делай `ORDER BY` без `FETCH FIRST` или `ROWNUM`
Не сортируй всё зря:


SELECT * FROM employees ORDER BY salary DESC FETCH FIRST 10 ROWS ONLY;


📦 7. Управляй CTE через `MATERIALIZE` и `INLINE`
Это может влиять на производительность:


WITH /*+ MATERIALIZE */ heavy_part AS (...)
SELECT * FROM heavy_part WHERE rownum = 1;


📊 8. Освой `MODEL` для расчётов как в Excel
Рядовые и прогнозные расчёты в SQL без курсоров:


SELECT * FROM sales
MODEL
PARTITION BY (region)
DIMENSION BY (month)
MEASURES (sales)
RULES (
sales[13] = sales[12] * 1.1
);


💡 Не ограничивайся SELECT — используй весь потенциал Oracle SQL.

👍 Лайк и сохраняй себе, чтобы не потерять

SQL Community | Чат
Please open Telegram to view this post
VIEW IN TELEGRAM
1👍134🤔2💊2🔥1🤨1
🧩 SQL-задача с подвохом (Oracle): рейтинг клиентов по заказам

У тебя есть таблица заказов:


CREATE TABLE orders (
order_id NUMBER PRIMARY KEY,
customer_id NUMBER,
amount NUMBER,
order_date DATE
);


Данные:

| order_id | customer_id | amount | order_date |
|----------|-------------|--------|------------|
| 1 | 101 | 500 | 01-JAN-24 |
| 2 | 102 | 300 | 02-JAN-24 |
| 3 | 103 | NULL | 02-JAN-24 |
| 4 | 101 | 700 | 03-JAN-24 |
| 5 | 102 | NULL | 04-JAN-24 |
| 6 | 104 | 400 | 05-JAN-24 |
| 7 | 103 | 200 | NULL |


🎯 Задача:
Напиши SQL-запрос, который для каждого customer_id вернёт:
• сумму всех заказов (`SUM(amount)`) — учитывай только строки, где amount не NULL
• количество заказов (включая `NULL`-значения)
• рейтинг клиента (`RANK()`), где самый высокий SUM(amount) получает ранг 1
• если у клиента SUM(amount) NULL, он должен получить последний ранг

⚠️ Подвох:
Oracle по умолчанию сортирует NULL в ORDER BY либо вверх, либо вниз в зависимости от ASC или DESC. Это влияет на оконную функцию RANK(). Если не указать NULLS LAST, клиент с NULL в SUM(amount) может получить ранг 1 — что неверно.

💡 Ожидаемый результат:

| customer_id | sum_amount | order_count | rank |
|-------------|------------|-------------|------|
| 101 | 1200 | 2 | 1 |
| 104 | 400 | 1 | 2 |
| 102 | 300 | 2 | 3 |
| 103 | 200 | 2 | 4 |

Решение:

```sql
SELECT
customer_id,
SUM(amount) AS sum_amount,
COUNT(*) AS order_count,
RANK() OVER (
ORDER BY SUM(amount) DESC NULLS LAST
) AS rank
FROM orders
GROUP BY customer_id;
```

🔍 Почему это работает:
SUM(amount) автоматически пропускает NULL, то есть считает только валидные суммы
COUNT(*) считает все строки, даже с NULL
RANK() сортирует по убыванию суммы, а NULLS LAST гарантирует, что клиенты без суммы окажутся в конце рейтинга

📌 Вывод:
Даже опытные аналитики иногда забывают, что
RANK() чувствителен к NULL и что Oracle требует явного указания NULLS LAST, чтобы избежать неочевидных ошибок.

SQL Community | Чат
Please open Telegram to view this post
VIEW IN TELEGRAM
👍83🔥2🥱1🤨1
🧠 Хитрая SQL-задача на Oracle: кто не продал — тот тоже в списке

У вас есть таблица sales:


CREATE TABLE sales (
salesman_id NUMBER,
region VARCHAR2(50),
amount NUMBER
);


Данные:

| salesman_id | region | amount |
|-------------|------------|--------|
| 101 | 'North' | 200 |
| 101 | 'North' | NULL |
| 102 | 'North' | 150 |
| 103 | 'North' | NULL |
| 104 | 'South' | 300 |
| 105 | 'South' | NULL |

🎯 Задача:
Вывести salesman_id тех продавцов, чья сумма продаж в своём регионе меньше средней по региону, учитывая только те записи, где `amount` не NULL.
Но — обязательно включать продавцов, у которых все продажи NULL, и считать, что их сумма равна 0.

---

### Подвохы:
- SUM() и AVG() игнорируют NULL, но если у человека *все* значения NULL, SUM вернёт NULL.
- Нужно сравнивать 0 с AVG, а не NULL.
- Надо корректно сгруппировать по региону и учитывать, где NULL'ы не попадают в AVG.

---

Решение:

```sql
SELECT s.salesman_id
FROM (
SELECT
salesman_id,
region,
NVL(SUM(amount), 0) AS total_sales
FROM sales
GROUP BY salesman_id, region
) s
JOIN (
SELECT
region,
AVG(amount) AS avg_region_sales
FROM sales
WHERE amount IS NOT NULL
GROUP BY region
) r
ON s.region = r.region
WHERE s.total_sales < r.avg_region_sales;
```

🧠 Разбор:

1. В подзапросе `s`:
- `SUM(amount)` по продавцу → `NULL`, если продаж нет
- `NVL(..., 0)` превращает такие NULL в 0

2. В подзапросе `r`:
- `AVG(amount)` по региону, игнорируя NULL
- Только валидные продажи участвуют в средней

3. Сравниваем: **продавцы с 0 продаж тоже идут в сравнение**

💡 Вывод:

Этот запрос:
- корректно считает продажи даже для "нулевых" продавцов
- использует `NVL()` и фильтрацию `WHERE amount IS NOT NULL`
- демонстрирует знание поведения агрегатных функций и подзапросов

👀 На собеседованиях часто забывают, что `SUM(NULL)` даёт `NULL`, и сравнение с `AVG` не срабатывает без `NVL`.


SQL Community | Чат
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5🥱32🔥1🤨1
🔥 Polars: шпаргалка

Polars ≠ Pandas. Это колоночный движок, вдохновлённый Rust и SQL. Никаких SettingWithCopyWarning — всё иммутабельно и параллелится.

🚀 Быстрый старт


import polars as pl

df = pl.DataFrame({
"id": [1, 2, 3],
"name": ["Alice", "Bob", "Charlie"],
"score": [95, 85, 100]
})


📊 Выборка и фильтрация


df.filter(pl.col("score") > 90)
df.select(pl.col("name").str.lengths())
df[df["id"] == 2]


• Комбинированные условия:

df.filter((pl.col("score") > 80) & (pl.col("name").str.contains("A")))


---

## ⚙️ Трансформации

• Вычисление новых колонок:

df.with_columns([
(pl.col("score") / 100).alias("percent"),
pl.col("name").str.to_uppercase().alias("name_upper")
])


• Удаление/переименование:

df.drop("id").rename({"name": "username"})


apply() — только если нельзя обойтись иначе:

df.with_columns(
pl.col("score").map_elements(lambda x: x * 2).alias("doubled")
)


🧠 Группировка и агрегаты



df.groupby("name").agg([
pl.col("score").mean().alias("avg_score"),
pl.count()
])


• Агрегация с кастомной функцией:

df.groupby("name").agg(
(pl.col("score") ** 2).mean().sqrt().alias("rms")
)


---

## 🪄 Ленивая обработка (LazyFrame)


lf = df.lazy()
result = (
lf
.filter(pl.col("score") > 90)
.with_columns(pl.col("score").log().alias("log_score"))
.sort("log_score", descending=True)
.collect()
)


Всё оптимизируется *до выполнения* — pushdown, predicate folding, projection pruning.

🔥 Joins


df1.join(df2, on="id", how="inner")


Варианты: "inner", "left", "outer", "cross", "semi", "anti"

📂 Работа с файлами


pl.read_csv("data.csv")
df.write_parquet("out.parquet")
pl.read_json("file.json", json_lines=True)


Ленивая загрузка:

pl.read_parquet("big.parquet", use_pyarrow=True).lazy()


---

## 🧮 Аналитика и окна


df.with_columns([
pl.col("score").rank("dense").over("group").alias("rank"),
pl.col("score").mean().over("group").alias("group_avg")
])


🧱 Структуры, списки, explode


df = pl.DataFrame({
"id": [1, 2],
"tags": [["a", "b"], ["c"]]
})
df.explode("tags")


• Работа с вложенными списками:

df.select(pl.col("tags").list.lengths())


🧪 Полезные фичи

• Проверка типов:

df.schema
df.dtypes


• Проверка на null:

df.filter(pl.col("score").is_null())


• Заполнение:

df.fill_null("forward")


• Выбор n лучших:

df.sort("score", descending=True).head(5)


📦 Советы и best practices

• Используй lazy() для производительности.

• Избегай .apply() — если можешь, используй pl.col().map_elements() или векторные выражения.

• Сохраняй schema — удобно при пайплайнах данных.

@pl.api.register_expr_namespace("yourns") — добавляй кастомные методы как namespace.


Polars: минимализм, скорость, безопасность.
12👍5🔥4🥱1