7786
Авторский канал про Базы Данных и SQL Ресурсы, гайды, задачи, шпаргалки. Информация ежедневно пополняется! Автор: @energy_it
😍 SQL PostgreSQL Course — информативный курс по SQL с практикой!
Это структурированное обучение по PostgreSQL: от базовых запросов до JOIN’ов, подзапросов и проектирования базы данных. Материал выстроен последовательно, поэтому легко идти шаг за шагом и не теряться в теме. Здесь есть практика с заданиями и ответами, за счёт чего обучение закрепляется.
Оставляю ссылочку: GitHub 📱
Дубликаты после JOIN — откуда берутся и как контролировать!
Одна из частых проблем — внезапное размножение строк после JOIN. Это базовое поведение, если не учтена кардинальность связей.
Таблицы:
orders(id, customer_id, amount)
payments(id, order_id, status)
SELECT
o.id,
o.amount,
p.status
FROM orders o
LEFT JOIN payments p
ON p.order_id = o.id;
orders — payments. То есть один заказ повторится столько раз, сколько у него записей в payments.SELECT
COUNT(*) AS total_orders
FROM orders o
LEFT JOIN payments p
ON p.order_id = o.id;
COUNT(*) считает строки уже после JOIN, а не заказы. Если у заказа 3 платежа — он попадёт в счёт 3 раза. Это одна из самых частых причин кривых метрик.SELECT
COUNT(DISTINCT o.id) AS total_orders
FROM orders o
LEFT JOIN payments p
ON p.order_id = o.id;
DISTINCT — это дополнительная операция, и на больших объёмах она может стоить дорого.payments, JOIN здесь вообще лишний. Лучше контролировать кардинальность до JOINEXISTS:SELECT
o.id,
o.amount
FROM orders o
WHERE EXISTS (
SELECT 1
FROM payments p
WHERE p.order_id = o.id
AND p.status = 'success'
);
EXISTS работает как semi-join: он проверяет факт наличия строки, но не тянет её в результат. За счёт этого одна строка заказа остаётся одной строкой.JOIN всё-таки нужен — агрегируем заранее:SELECT
o.id,
o.amount,
COALESCE(p.has_success, 0) AS has_success
FROM orders o
LEFT JOIN (
SELECT
order_id,
MAX(CASE WHEN status = 'success' THEN 1 ELSE 0 END) AS has_success
FROM payments
GROUP BY order_id
) p ON p.order_id = o.id;
payments к одной строке на order_id, и только потом делаем JOIN. После этого результат становится понятным: одна строка на заказ, без раздувания.GROUP BY o.id, o.amount, p.status
GROUP BY просто фиксирует текущую детализацию. Если у заказа было несколько статусов — строки никуда не денутся.JOIN не создаёт дубликаты сам по себе. Он возвращает строки в соответствии с числом совпадений по условию ON. Если после JOIN строк стало больше — значит реальная связь между таблицами не 1:1, а 1:N или даже N:M.
☕️ Полезную статью нашёл на Хабре: «Ваш RAG не умеет думать. А мой умеет»!
В этой статье:• Разбирается, почему классические RAG-системы на базе векторных БД часто ограничиваются поверхностным поиском;
• Показан подход HippoRAG 2, где память и связи между фактами организованы ближе к графовой модели;
• Объясняется, как эволюционирует работа с данными;
• Рассматривается, как это влияет на SQL/NoSQL слой, архитектуру хранения и построение более осмысленных запросов к данным.
🔊 Продолжайте читать на Habr!
Проверяем дубликаты и считаем уникальные значения!
В больших таблицах важно быстро находить повторяющиеся записи и понимать, сколько уникальных элементов. Это полезно для контроля качества данных и аналитики.
Создадим таблицу пользователей:
CREATE TABLE users (
user_id INT,
email VARCHAR(100)
);
INSERT INTO users VALUES
(1, 'alice@mail.com'),
(2, 'bob@mail.com'),
(3, 'alice@mail.com'),
(4, 'carol@mail.com'),
(5, 'bob@mail.com');
SELECT email, COUNT(*) AS cnt,
CASE WHEN COUNT(*)>1 THEN 'Duplicate' ELSE 'Unique' END AS status
FROM users
GROUP BY email;
COUNT() + GROUP BY группирует одинаковые значения, а CASE сразу классифицирует их как дубликаты или уникальные.email | cnt | status
-----------------------------
alice@mail.com | 2 | Duplicate
bob@mail.com | 2 | Duplicate
carol@mail.com | 1 | Unique
❤️ Нашел вам html5css — простой и наглядный справочник по SQL на русском!
Если хочется быстро разобраться в командах — этот сайт отлично подойдёт. Конструкции объяснены на примерах, с чётким и кратким синтаксисом. Удобно использовать как справочник или экспресс‑повторение.
📌 Оставляю ссылочку: html5css.ru
➡️ SQL Ready | #ресурс
Совет на 2026 год — переходите в ML.
Пока обычные разрабы конкурируют с ИИ-копилотами, ML-инженеры эти самые нейронки создают.
В эпоху нейростей это самые востребованые люди в мире программирования. Зарплаты мидлов начинаются от 250 000 ₽, а у сеньоров в BigTech доходят до 700 000 ₽.
А чтобы освоить его всего за 4 месяца без лишней суеты — изучите канал Артема Алехина.
Его бэкграунд: Руководитель команды в Сбере, валютная удаленка. К 22 годам вышел на доход 1 000 000+ ₽ в месяц.
На канале вы найдёте:
— Всё про самые востребованные стеки(Python, ИИ-агенты, NLP) и почему математика — это не страшно, если учить только нужное.
— Как оформить резюме, чтобы оно пролетало через любые LLM-фильтры и ATS-системы прямо к тимлидам.
— Скрипты переговоров, которые помогли его ученикам прыгнуть с 0 до 360к всего за 8 месяцев.
Во времена острой нехватки ML-разработчиков, это лучшее время, чтобы перекатиться. Переходи и изучай: /channel/+qG_ihzUEkHJlOWVi
🖥 Когда система нагружена сильнее всего?
В системах с временными интервалами важно понимать не просто отдельные события, а их наложение — именно оно определяет реальную нагрузку.
Сегодня в задаче:
• Преобразуем интервалы в точки начала и конца, чтобы работать с ними как с потоком событий;
• Посчитаем текущую нагрузку через накопительную сумму по времени;
• Найдём момент максимального количества одновременных событий — пик нагрузки системы.
☕️ Годную статью нашёл на Хабре: «Как работает распределённый SQL в YDB: от запроса до выполнения»!
В этой статье:• Показано, как в YDB обрабатывается SQL-запрос — от парсинга до распределённого исполнения по узлам;
• Разбирается, как устроены планировщик, оптимизатор и механизмы шардинга при работе с большими данными;
• Объясняется, как достигаются консистентность, отказоустойчивость и масштабируемость в распределённой базе.
🔊 Продолжайте читать на Habr!
📂 Напоминалка по блокировкам и транзакциям в SQL!
Optimistic locking позволяет работать без блокировок — конфликт проверяется при записи (например, через version или updated_at). Pessimistic locking наоборот сразу ставит блокировку (SELECT ... FOR UPDATE) и заставляет другие транзакции ждать.
На картинке — как два подхода ведут себя при одновременном обновлении одной строки: в одном случае получаем conflict, в другом — очередь.
Сохрани, чтобы не потерять!
➡️ SQL Ready | #ресурс
🧐 SQL Syntax Cheat Sheet — справочник по SQL-синтаксису!
Универсальная шпаргалка по SQL, где собраны все основные конструкции языка. Всё структурировано по разделам, поэтому можно быстро найти нужный синтаксис. Формат максимально практичный: команда, пример, объяснение, что позволяет не просто смотреть, а сразу понимать, как что применяется в запросах.
Оставляю ссылочку: GitHub 📱
😎 itProger — лаконичный справочник и курс по SQL
Если нужно быстро освежить синтаксис или понять суть команд — это то, что нужно. Все основные конструкции, примеры и видеоуроки — коротко и по делу. Отлично подойдёт как шпаргалка и мини‑курс.
📌 Оставляю ссылочку: itproger.com/SQL
➡️ SQL Ready | #ресурс
📂 Напоминалка по сетям!
Каждый уровень играет важную роль: от физической передачи сигналов до приложений, с которыми мы взаимодействуем каждый день. Понимание этой модели помогает лучше разбираться в сетевых ошибках, маршрутизации и защите данных.
На картинке — 7 уровней OSI, что делает каждый из них и примеры протоколов.
Сохрани, чтобы не забыть!
➡ SQL Ready | #ресурс
👍 Mindgrasp AI — инструмент для быстрого анализа и усвоения информации!
Это AI-ассистент для обучения, который позволяет загружать документы, видео или аудио и автоматически превращать их в структурированные материалы: краткие конспекты, ответы, карточки и тесты. Также можно задавать вопросы прямо по загруженному источнику и получать точные ответы на основе его содержания.
📌 Оставляю ссылочку: mindgrasp.ai
➡️ SQL Ready | #ресурс
📂 Напоминалка по SQL вопросам!
Например, WHERE фильтрует строки до агрегации, а HAVING — уже после GROUP BY.
На картинке — основные конструкции: разница между WHERE и HAVING, оконные функции против обычной агрегации, а также основы партицирования для больших таблиц.
Сохрани, чтобы не потерять!
➡️ SQL Ready | #ресурс
😎 SQL Tutorials — структурированный набор материалов!
Здесь собраны конспекты, шпаргалки и учебные PDF по всем ключевым темам: от базовых запросов до сложных конструкций и оптимизации. Отдельно выделяется наличие большого количества материалов для подготовки к собеседованиям, сотни вопросов и разборов.
Оставляю ссылочку: GitHub 📱
😎 SQL Server Kit — практическая база инструментов и материалов!
Репозиторий представляет собой обширную коллекцию скриптов, статей, рекомендаций и утилит для администрирования и разработки на SQL. Внутри собраны решения для диагностики производительности, оптимизации запросов, мониторинга, резервного копирования, работы с индексами и анализа состояния базы данных. Также представлены ссылки на проверенные источники и лучшие практики работы с СУБД.
Оставляю ссылочку: GitHub 📱
Совет на всю жизнь — начните изучать вайбкодинг.
Нейронки уже собирают проекты от идеи до релиза, пишут код, находят и исправляют баги лучше команды айтишников. Это сэкономит вам десятки часов работы.
А чтобы научиться вайбкодить и не совершать ошибки - читайте канал AI-архитектор, где есть:
➖Инструкции по Antigravity и n8n.
➖Связки, которые приносят от 100 000 ₽ за проект
➖Реальный опыт программиста и вайбкодера, который настраивает автоматизацию ИИ для гос. компаний.
Подписывайтесь и смотрите в закрепе, как за 2 дня собрать ИИ-автоматизацию без единой строчки кода: /channel/+eBc7ivvcY34xZTli
Использование data-modifying CTE для цепочек операций в одном запросе!
Обычно изменения и чтение делают разными запросами, что создаёт лишние round-trip и риск рассинхронизации данных между операциями:
UPDATE orders
SET status = 'processing'
WHERE id = 123
RETURNING *;
RETURNING уже даёт доступ к изменённым строкам, но data-modifying CTE позволяет пойти дальше и использовать их в следующих шагах:WITH updated AS (...)
CTE фиксирует результат изменения и гарантирует, что последующие операции работают с тем же набором строк в рамках одного statement:INSERT INTO audit_log (order_id, new_status)
SELECT id, status
FROM updated;
SELECT.data-modifying CTE — это способ строить сложные, но безопасные цепочки операций внутри одного SQL-запроса.
📂 Напоминалка по реляционной модели и SQL!
Например, Primary Key гарантирует уникальность записей, Foreign Key — обеспечивает ссылочную целостность, а JOIN’ы позволяют собирать распределённые данные в единую выборку.
На картинке — основные концепции, которые используются при проектировании и работе с БД.
Сохрани, чтобы не потерять!
➡️ SQL Ready | #ресурс
🖥 Напоминалка по продвинутым функциям работы с датами!Эта шпаргалка собрала ключевые методы, которые позволяют не просто сравнивать даты, а точно извлекать нужные части, округлять по нужному уровню, форматировать для отчётов и рассчитывать интервалы в удобной форме.
➡️ SQL Ready | #шпора
😎 SQLServerCentral — крупнейшее сообщество и база знаний по Microsoft SQL Server!
Здесь публикуются ежедневные статьи, обучающие серии Stairway, подборки скриптов, обзоры книг, а также активные форумы и блоги для администраторов БД и разработчиков.
📌 Оставляю ссылочку: sqlservercentral
➡️ SQL Ready | #ресурс
🔥База платных курсов и книг по программированию на весь 2026 год!🔥
Канал новый, потому что слишком много людей ждут халявы.
Тут всё, что обычно стоит тысячи:
— Авторские курсы, которые не найти в открытом доступе
— Самые свежие книги для старта
— Всё, что нужно, чтобы за полгода вырасти от мидла до сеньора
➡️ Залетайте, пока есть доступ!
Почему после JOIN внезапно растут агрегаты!
Одна из самых неприятных ошибок в аналитическом SQL — когда запрос выглядит нормально, всё отрабатывает без ошибок, а цифры в итоге получаются больше, чем должны быть.
Классическая ситуация. Есть таблицы:
orders(id, customer_id, amount)
order_items(id, order_id, product_id, quantity)
SELECT SUM(o.amount) AS total_revenue
FROM orders o
JOIN order_items i
ON i.order_id = o.id;
order_items, то строка из orders после JOIN повторится 3 раза. И o.amount тоже попадёт в расчёт 3 раза. В итоге сумма завышается. Это как раз тот самый fan-out: одна строка размножается после JOIN.SELECT
COUNT(*) AS rows_after_join,
COUNT(DISTINCT o.id) AS unique_orders
FROM orders o
JOIN order_items i
ON i.order_id = o.id;
JOIN стало больше, чем уникальных заказов, значит у вас fan-out по уровню orders. Что делать правильно — зависит от задачи.JOIN вообще не нужен:SELECT SUM(amount)
FROM orders;
JOIN нужен только для фильтрации, например по конкретному товару, безопаснее использовать EXISTS:SELECT SUM(o.amount)
FROM orders o
WHERE EXISTS (
SELECT 1
FROM order_items i
WHERE i.order_id = o.id
AND i.product_id = 10
);
orders не ломается. Один заказ остаётся одной строкой.order_items:SELECT SUM(o.amount)
FROM orders o
JOIN (
SELECT DISTINCT order_id
FROM order_items
WHERE product_id = 10
) i ON i.order_id = o.id;
order_items:SELECT SUM(i.quantity)
FROM order_items i;
SELECT SUM(DISTINCT o.amount)
FROM orders o
JOIN order_items i
ON i.order_id = o.id;
DISTINCT сейчас всё починит, на деле — нет. Почему это плохая идея: если у двух разных заказов одинаковый amount, один из них просто схлопнется; запрос начинает давать вроде бы правдоподобный, но неверный результат.JOIN не один, а цепочка: orders — order_items — products — categoriesSELECT COUNT(*) FROM orders;
SELECT COUNT(*)
FROM orders
JOIN order_items ON order_items.order_id = orders.id;
SELECT COUNT(*)
FROM orders
JOIN order_items ON order_items.order_id = orders.id
JOIN products ON products.id = order_items.product_id;
JOIN начинаются лишние строки.JOIN, всегда держите в голове гранулярность данных. Что у вас является одной строкой: заказ, позиция заказа, клиент? Сначала определяете уровень данных — потом джойните и агрегируете.
Как вернуть строки в том же порядке, в котором пришли id?
Когда из приложения прилетает список id, обычный WHERE id = ANY(...) находит нужные строки, но порядок входного массива не сохраняет:
SELECT *
FROM users
WHERE id = ANY(ARRAY[42, 7, 99]);
WITH ORDINALITY добавляет каждой строке её позицию во входном наборе:unnest(ARRAY[42, 7, 99]) WITH ORDINALITY
ORDER BY x.ord
CASE, без ручной сортировки в коде и без лишнего постобработчика.WITH ORDINALITY — это простой способ сохранить порядок входных данных в SQL, он хорошо заходит в API и массовые выборки.
🖥 Разбираемся с FILTER — лаконичные агрегаты по условию!FILTER позволяет задать условие прямо для SUM, COUNT, AVG — без вложенных подзапросов и лишнего шума. Код получается чище, короче и проще читается.
Что важно знать:
• FILTER работает внутри агрегата — условие применяется только к нему.
• Отлично подходит для отчётных таблиц с множеством условий.
• Заменяет CASE WHEN в 90% ситуаций, где раньше казалось без него никак.
Проверка качества и целостности данных!
В больших продакшн-базах важно не только находить ошибки, но и структурировать их: проверять NULL, дубликаты, некорректные форматы и аномальные значения.
Сначала выявляем строки с пустыми ключевыми полями:
SELECT user_id, email, created_at
FROM users
WHERE user_id IS NULL
OR email IS NULL;
SELECT email, COUNT(*) AS cnt,
CASE WHEN COUNT(*)>1 THEN 'Duplicate' ELSE 'Unique' END AS status
FROM users
GROUP BY email;
SELECT order_id, total_amount
FROM orders
WHERE total_amount < 0;
Уникальность с NULL без триггеров и костылей!
Обычный UNIQUE в SQL пропускает несколько NULL, потому что NULL не считается равным другому. Из-за этого ограничение часто формально есть, а правило на самом деле не соблюдается:
UNIQUE (telegram_id)
UNIQUE даёт дыру в данных.UNIQUE NULLS NOT DISTINCT (telegram_id)
NULL обычным сравнимым значением именно для проверки уникальности.NULL уже не пройдёт, как и дубликат обычного значения.UNIQUE NULLS NOT DISTINCT (tenant_id, external_id)
nullable внешних идентификаторов, one-to-one связей, интеграционных ключей и любых полей, где NULL тоже должен быть единственным допустимым состоянием.UNIQUE NULLS NOT DISTINCT закрывает один из источников грязных данных и заменяет триггеры.
Индексы и ORDER BY DESC без лишней сортировки!
Многие думают, что для ORDER BY … DESC нужен отдельный DESC-индекс — но это не всегда так.
В PostgreSQL обычный B-tree индекс:
CREATE INDEX idx_orders_user_created
ON orders (user_id, created_at);
ORDER BY created_at DESC через backward scan, без дополнительной сортировки.user_id ASC, created_at DESC)), тогда имеет смысл явно указать направление:CREATE INDEX idx_orders_user_created_desc
ON orders (user_id, created_at DESC);
UPDATE ... FROM в PostgreSQL: как обновлять данные из другой таблицы и не поймать скрытые дубли!
Задача, которая встречается постоянно: обновить таблицу по данным из другой. Например, проставить клиентам дату последнего заказа.
Есть таблицы:
customers(id, last_order_at)
orders(id, customer_id, created_at)
UPDATE customers c
SET last_order_at = o.created_at
FROM orders o
WHERE o.customer_id = c.id;
last_order_at? Ответ — это не гарантируется.JOIN даёт несколько строк для одной записи в customers, PostgreSQL не гарантирует, какую строку он использует при обновлении.UPDATE customers c
SET last_order_at = t.max_created_at
FROM (
SELECT
customer_id,
MAX(created_at) AS max_created_at
FROM orders
GROUP BY customer_id
) t
WHERE t.customer_id = c.id;
DISTINCT ON:UPDATE customers c
SET last_order_at = t.created_at
FROM (
SELECT DISTINCT ON (customer_id)
customer_id,
created_at
FROM orders
ORDER BY customer_id, created_at DESC
) t
WHERE t.customer_id = c.id;
created_at, лучше добавить tie-breaker:ORDER BY customer_id, created_at DESC, id DESC
UPDATE customers c
SET last_order_at = (
SELECT MAX(o.created_at)
FROM orders o
WHERE o.customer_id = c.id
);
customers, и у клиентов без заказов будет NULL.UPDATE customers c
SET last_order_at = t.created_at
FROM (
SELECT customer_id, created_at
FROM orders
) t
WHERE t.customer_id = c.id;
UPDATE на SELECT с тем же JOIN и посмотрите, есть ли дубли:SELECT c.id, t.*
FROM customers c
JOIN ...
UPDATE уже небезопасен. Про индексы:CREATE INDEX idx_orders_customer_created
ON orders (customer_id, created_at);
MAX, и для ORDER BY. Итог: UPDATE ... FROM — крутой инструмент, но он не проверяет однозначность соответствия.JOIN возвращает несколько строк на одну обновляемую запись, результат не гарантируется. Сначала фиксируем одну строку на ключ — потом обновляем.
✍️ SQL for Beginners — база SQL с нуля на практике!
Репозиторий с материалами для изучения SQL с самого начала: здесь разбираются основы работы с базами данных, синтаксис запросов, JOIN-ы, фильтрация, агрегации и структура таблиц. Формат обучения построен вокруг практики, примеры запросов, задания и объяснения помогают не просто читать теорию, а сразу писать код.
Оставляю ссылочку: GitHub 📱