Data Science. SQL hub
36K subscribers
1.2K photos
100 videos
37 files
1.2K links
По всем вопросам- @workakkk

@itchannels_telegram - 🔥лучшие ит-каналы

@ai_machinelearning_big_data - Machine learning

@pythonl - Python

@pythonlbooks- python книги📚

@datascienceiot - ml книги📚

РКН: https://vk.cc/cIi9vo

#VRHSZ
Download Telegram
SQL-ловушка: этот UPDATE может незаметно испортить данные в продакшене

Код корректный, PostgreSQL не выдаст ошибку. Но результат не так очевиден.

Есть таблица товаров:


CREATE TABLE products (
id INT PRIMARY KEY,
price INT
);

INSERT INTO products VALUES (1, 100);


Применяем скидки:


WITH discounts(id, amount) AS (
VALUES (1, 10), (1, 20)
)
UPDATE products p
SET price = p.price - d.amount
FROM discounts d
WHERE p.id = d.id;

SELECT price FROM products;


Что вернёт последний SELECT?

А) 90
Б) 80
В) 70
Г) 80 или 90, результат не гарантирован

Один запрос, две скидки на один товар. Как PostgreSQL обработает такой UPDATE?

Подвох связан с поведением UPDATE ... FROM, о котором легко забыть даже при работе с реальными базами.
👍11❤6🔥1
ORM превратила один SQL-запрос в 51. Разработчик даже не заметил

Знакомая ситуация: код выглядит чисто, никаких SQL-запросов вручную, всё через ORM.

Например, в Django:


posts = Post.objects.all()[:50]

for post in posts:
print(post.author.name)


Кажется, что мы просто получаем 50 постов с авторами. Но на практике это может вызвать 51 запрос к базе данных.

Один запрос загружает посты, а ещё 50 отдельно получают авторов.

Это классическая проблема N+1 queries. Причём чем больше записей, тем больше обращений к БД.

Исправляется одной строкой:


posts = Post.objects.select_related("author")[:50]


Теперь Django использует JOIN и получает посты вместе с авторами одним запросом.

ORM избавляет от написания SQL, но не от необходимости понимать, какой SQL выполняется.

Именно поэтому при оптимизации API полезнее сначала посмотреть на количество запросов, чем переписывать Python-код ради нескольких миллисекунд.
👍7❤3🔥2😁1
🖥️ Черная пятница на Stepik на один из лучших курсов по контейнеризации!

Если давно хотел разобраться в теме, но постоянно откладывал или терялся среди десятков материалов, этот курс на Stepik поможет выстроить обучение последовательно.

Что получишь:
- Понятные объяснения без лишней воды.
- Практические задания для закрепления знаний.
- Возможность учиться в удобном темпе.
- Структурированный подход вместо хаотичного поиска информации.

Подойдёт тем, кто начинает с нуля или хочет систематизировать знания.

Начни прокачивать навыки уже сейчас:

https://stepik.org/course/303477/
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3❤2👎2🔥2
🖥️ SQL-задача с подвохом: почему COUNT(*) считает неправильно?

Даже опытные разработчики иногда ошибаются с оконными функциями PostgreSQL.

Есть таблица событий:


CREATE TABLE events (
id INT PRIMARY KEY,
user_id INT,
ts TIMESTAMP
);

INSERT INTO events VALUES
(1, 10, '2026-01-01 10:00'),
(2, 10, '2026-01-01 10:05'),
(3, 10, '2026-01-01 10:05'),
(4, 10, '2026-01-01 10:20');


Выполняем запрос:


SELECT
id,
COUNT(*) OVER (
PARTITION BY user_id
ORDER BY ts
) AS cnt
FROM events
ORDER BY id;


Что вернёт PostgreSQL?

А) 1, 2, 3, 4

Б) 1, 3, 3, 4

В) 4, 4, 4, 4

Г) 1, 2, 2, 4

Правильный ответ: Б.

По умолчанию PostgreSQL использует оконную рамку:

```sql
RANGE BETWEEN UNBOUNDED PRECEDING
AND CURRENT ROW
```

При
RANGE строки с одинаковым значением ts считаются одной группой (peers). Поэтому обе строки с 10:05 получают cnt = 3.

Чтобы получить обычный накопительный счётчик, нужно явно указать
ROWS и обеспечить однозначную сортировку:

```sql
SELECT
id,
COUNT(*) OVER (
PARTITION BY user_id
ORDER BY ts, id
ROWS BETWEEN UNBOUNDED PRECEDING
AND CURRENT ROW
) AS cnt
FROM events
ORDER BY id;
```

Теперь результат:
1, 2, 3, 4.

Подвох:
COUNT(*) OVER (ORDER BY ...) не всегда считает строки по одной. Всё зависит от оконной рамки и повторяющихся значений сортировки.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍13❤5🔥4
SQL-задача с подвохом: найдите пользователей, которые вернулись после 30 дней молчания

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

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


CREATE TABLE events (
user_id INT,
event_time TIMESTAMP
);

INSERT INTO events VALUES
(1, '2026-10-01 10:00'),
(1, '2026-10-01 12:00'),
(1, '2026-11-01 09:00'),
(1, '2026-11-02 14:00'),
(2, '2026-10-01 10:00'),
(2, '2026-10-31 10:00'),
(3, '2026-09-01 10:00'),
(3, '2026-11-05 10:00'),
(3, '2026-11-05 11:00');


Задача: для каждого пользователя найти все даты возвращения после 30 полных календарных дней без активности.

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

| user_id | return_date |
|---|---|
| 1 | 2026-11-01 |
| 3 | 2026-11-05 |

Условия: несколько событий в один день считаются одной активностью. День последней активности и день возвращения в период молчания не входят. Например, между 1 октября и 31 октября только 29 полных дней без активности, а между 1 октября и 1 ноября уже 30.

Ограничение: решите задачу одним SQL-запросом, без процедур, временных таблиц и циклов.

На первый взгляд достаточно LAG(), но если не учесть дубли событий и границы календарных дней, результат окажется неправильным.

Как бы вы решили эту задачу на PostgreSQL?
👍7🔥5❤2🥰1