SQL Ready | Базы Данных
Авторский канал про Базы Данных и SQL Ресурсы, гайды, задачи, шпаргалки. Информация ежедневно пополняется! Автор: @energy_it РКН: https://clck.ru/3QREBc Реклама на бирже: https://telega.in/c/sql_ready
显示更多📈 Telegram 频道 SQL Ready | Базы Данных 的分析概览
频道 SQL Ready | Базы Данных (@sql_ready) 俄语 语言赛道中的 是活跃参与者。目前社区聚集了 15 560 名订阅者,在 技术与应用 类别中位列第 8 395,并在 俄罗斯 地区排名第 43 172 位。
📊 受众指标与增长动态
自 невідомо 创建以来,项目保持高速增长,吸引了 15 560 名订阅者。
根据 10 六月, 2026 的最新数据,频道保持稳定运转。过去 30 天订阅人数变化为 57,过去 24 小时变化为 -8,整体触达仍然可观。
- 认证状态: 未认证
- 互动率 (ER): 平均受众互动率为 11.95%。内容发布后 24 小时内通常能获得 6.07% 的反应,占订阅者总量。
- 帖子覆盖: 每篇帖子平均可获得 1 860 次浏览,首日通常累积 945 次浏览。
- 互动与反馈: 受众积极参与,单帖平均反应数为 25。
- 主题关注点: 内容集中在 sql, строка, user_id, created_at, desc 等核心主题上。
📝 描述与内容策略
作者将该频道定位为表达主观观点的平台:
“Авторский канал про Базы Данных и SQL
Ресурсы, гайды, задачи, шпаргалки.
Информация ежедневно пополняется!
Автор: @energy_it
РКН: https://clck.ru/3QREBc
Реклама на бирже: https://telega.in/c/sql_ready”
凭借高频更新(最新数据采集于 11 六月, 2026),频道始终保持新鲜度与高覆盖。分析显示受众积极互动,使其成为 技术与应用 类别中的关键影响点。
ORDER BY всегда сортирует вообще всю таблицу. Но в PostgreSQL для LIMIT есть отдельная оптимизация — Top-N Heap Sort.
SELECT *
FROM orders
ORDER BY created_at DESC
LIMIT 10;
Если нужен только top-10 результат, PostgreSQL не обязан сортировать миллионы строк полностью. Вместо этого он держит в памяти только N лучших строк во время scan.
Это видно прямо в execution plan:
Sort Method: top-N heapsort
Особенно интересно становится на огромных таблицах, где полный sort мог бы уйти на диск:
SET work_mem = '4MB';
EXPLAIN ANALYZE
SELECT *
FROM events
ORDER BY created_at DESC
LIMIT 50;
Даже при маленьком work_mem PostgreSQL может избежать полной сортировки всех строк и работать заметно быстрее именно благодаря оптимизации Top-N.
А если добавить подходящий индекс:
CREATE INDEX idx_events_created_at
ON events (created_at DESC);
🔥 PostgreSQL вообще сможет обойтись без Sort и просто сделать Index Scan в нужном порядке.
➡️ SQL Ready | #советОставляю ссылочку: GitHub 📱➡️ SQL Ready | #репозиторий
NOT IN при наличии NULL. Запрос выглядит абсолютно корректно, но внезапно перестаёт возвращать строки.
Таблицы:
users(id)
bans(user_id)
Допустим, нужно получить пользователей, которых нет в bans. Часто пишут так:
SELECT *
FROM users
WHERE id NOT IN (
SELECT user_id
FROM bans
);
Пока в bans.user_id нет NULL — всё работает нормально. Но представим данные:
bans
-------
1
2
NULL
Теперь запрос внезапно вернет:
0 rows
Хотя пользователи без бана есть. Почему так происходит — SQL сравнивает условие примерно как:
id <> 1
AND id <> 2
AND id <> NULL
Но:
id <> NULL
не даёт TRUE или FALSE. Результат: UNKNOWN. А в WHERE проходят только TRUE. Из-за этого всё условие целиком перестаёт выполняться.
Это особенность трёхзначной логики SQL: TRUE, FALSE, UNKNOWN
Любое сравнение с NULL даёт UNKNOWN:
NULL = 1
NULL <> 1
NULL = NULL
Поэтому NOT IN с NULL внутри подзапроса становится опасным.
Безопасный вариант — NOT EXISTS:
SELECT *
FROM users u
WHERE NOT EXISTS (
SELECT 1
FROM bans b
WHERE b.user_id = u.id
);
Почему EXISTS работает нормально: сравнение идёт построчно; NULL не ломает всю проверку; оптимизатор обычно хорошо превращает это в anti-join.
Ещё вариант — явно убрать NULL:
SELECT *
FROM users
WHERE id NOT IN (
SELECT user_id
FROM bans
WHERE user_id IS NOT NULL
);
Но на практике: NOT EXISTS обычно считается более безопасным и читаемым решением.
Отдельный момент: NOT IN () и NOT EXISTS могут давать разные планы выполнения в зависимости от СУБД. Но логически для nullable данных: NOT EXISTS почти всегда предпочтительнее.
Быстрая проверка проблемы:
SELECT COUNT(*)
FROM bans
WHERE user_id IS NULL;
Если такие строки есть — NOT IN уже потенциально опасен.
🔥 NOT IN и NULL плохо сочетаются. Если в подзапросе появляется хотя бы один NULL, условие может перестать возвращать строки вообще. Для таких проверок надёжнее использовать NOT EXISTS.
➡️ SQL Ready | #практикаА при мыслях об автоматизации задач и усилении своих скиллов, осекаешься - повышение квалифкации стоит десятки тысяч.
Больше 60% работодателей уже заложили бюджет на развитие команды в этом году. Просто большинство аналитиков никогда не просят. Потому что не знают, как.
Мы собрали полный гайд:
→ 8 шагов от «хочу прокачаться» до «компания всё оплатила»
→ Готовые обоснования для руководителя
→ Скрипты ответов на возражения
→ Чек-лист перед разговором с HR
Здесь - твоя грамотная заявка на корпоративный грант.
📄 Забирай методичку бесплатно → https://tglink.io/0516683a1ec128
Реклама. ООО "АЙТИ РЕЗЮМЕ". ИНН 4025460134. erid: 2W5zFHW9GuFEXPLAIN ANALYZE показывает время выполнения, но почти ничего не говорит о том, сколько данных было прочитано с диска и из памяти.
EXPLAIN (ANALYZE, BUFFERS)
SELECT ...
Опция BUFFERS добавляет статистику по страницам памяти:
shared hit — страницы найдены в PostgreSQL shared buffers.
shared read — страницы пришлось загрузить в shared buffers; это может быть физический диск или OS page cache.
Buffers: shared hit=15000 read=2
Такой запрос почти полностью работает из памяти, что хорошо.
А здесь видно узкое место: запрос массово читает диск, даже если по времени более менее.
Buffers: shared hit=10 read=15000
🔥 Два запроса с одинаковым execution time могут иметь абсолютно разную нагрузку на систему.
➡️ SQL Ready | #советorders(id, created_at)
Плохой вариант:
SELECT
DATE(created_at) AS day,
COUNT(*) AS orders_count
FROM orders
GROUP BY DATE(created_at)
ORDER BY day;
Проблема: если в какой-то день не было заказов — строки просто не будет. В аналитике это почти всегда ошибка, потому что отсутствие строки не равно ноль значений.
Попытка через LEFT JOIN сама по себе не помогает — без таблицы дат нечего достраивать. Правильный подход — сначала явно сгенерировать календарь, а потом присоединить данные.
В PostgreSQL для этого есть generate_series:
WITH calendar AS (
SELECT generate_series(
DATE '2026-01-01',
DATE '2026-01-10',
INTERVAL '1 day'
)::date AS day
)
SELECT
c.day,
COUNT(o.id) AS orders_count
FROM calendar c
LEFT JOIN orders o
ON DATE(o.created_at) = c.day
GROUP BY c.day
ORDER BY c.day;
Теперь: каждый день присутствует; если заказов нет — COUNT(o.id) вернёт 0.
Почему именно COUNT(o.id), а не COUNT(*): COUNT(*) посчитает строку календаря даже без совпадений — всегда ≥ 1; COUNT(o.id) считает только реальные заказы — корректный 0 при отсутствии данных.
Ещё один важный момент — условие в JOIN: ON DATE(o.created_at) = c.day. Так писать удобно, но это ломает использование индекса по created_at.
Лучше диапазон:
ON o.created_at >= c.day
AND o.created_at < c.day + INTERVAL '1 day'
Это позволяет использовать индекс и ускоряет запрос на больших таблицах.
Расширение задачи — накопительный итог по дням (используется тот же CTE calendar):
SELECT
c.day,
COUNT(o.id) AS orders_count,
SUM(COUNT(o.id)) OVER (ORDER BY c.day) AS running_total
FROM calendar c
LEFT JOIN orders o
ON o.created_at >= c.day
AND o.created_at < c.day + INTERVAL '1 day'
GROUP BY c.day
ORDER BY c.day;
Если нужно ограничение по периоду по данным, а не вручную:
WITH bounds AS (
SELECT
MIN(created_at)::date AS min_day,
MAX(created_at)::date AS max_day
FROM orders
),
calendar AS (
SELECT generate_series(min_day, max_day, INTERVAL '1 day')::date AS day
FROM bounds
)
SELECT ...
Календарь строится автоматически по данным. Если created_at — timestamptz, границы дня зависят от timezone.
🔥 Если в отчёте есть время — сначала формируйте ось времени, потом присоединяйте данные. Иначе вы работаете не с нулями, а с отсутствием строк, что даёт искажённую картину.
➡️ SQL Ready | #практикаLAG() позволяет получить значение из предыдущей строки внутри группы. Это один из самых полезных инструментов для аналитики, логов и временных рядов.
Таблица:
payments(id, user_id, amount, created_at)
Посмотрим предыдущий платёж каждого пользователя:
SELECT
user_id,
amount,
LAG(amount) OVER (
PARTITION BY user_id
ORDER BY created_at
) AS prev_amount
FROM payments;
LAG() сдвигает значение на одну строку назад внутри окна пользователя.
Теперь можно сразу посчитать разницу между текущим и прошлым платежом:
SELECT
user_id,
amount,
amount - LAG(amount) OVER (
PARTITION BY user_id
ORDER BY created_at
) AS diff
FROM payments;
Так удобно анализировать рост, падение и аномалии в данных без дополнительных JOIN.
Можно находить моменты изменения значения, например смену статуса:
SELECT *
FROM (
SELECT
user_id,
status,
LAG(status) OVER (
PARTITION BY user_id
ORDER BY created_at
) AS prev_status
FROM user_logs
) t
WHERE status <> prev_status;
Запрос покажет только строки, где статус изменился относительно предыдущего события.
🔥 LAG() часто используют для анализа трендов, поиска изменений, расчёта дельт и работы с временными рядами.
➡️ SQL Ready | #практикаОставляю ссылочку: GitHub 📱➡️ SQL Ready | #репозиторий
SELECT 1
FROM bookings
WHERE room_id = 101
AND start_at < $end
AND end_at > $start;
Даже если проверка есть, два параллельных запроса могут пройти её одновременно и записать конфликтующие данные:
CREATE EXTENSION IF NOT EXISTS btree_gist;
PostgreSQL решает это на уровне ограничений через exclusion constraint, который работает корректно даже при конкуренции:
ALTER TABLE bookings
ADD CONSTRAINT no_overlap
EXCLUDE USING gist (
room_id WITH =,
tsrange(start_at, end_at) WITH &&
);
🔥 exclusion constraint — это способ переложить сложную логику (пересечения, диапазоны, уникальность по условиям) с приложения на уровень СУБД с полной защитой от состояния гонки.
➡️ SQL Ready | #совет
现已上线!2025 年 Telegram 研究 — 年度关键洞察 
