sql_ready | Unsorted

Telegram-канал sql_ready - SQL Ready | Базы Данных

7786

Авторский канал про Базы Данных и SQL Ресурсы, гайды, задачи, шпаргалки. Информация ежедневно пополняется! Автор: @energy_it

Subscribe to a channel

SQL Ready | Базы Данных

UPDATE без холостых перезаписей!

Обычный UPDATE может перезаписывать строку, даже если значение не изменилось.

IS DISTINCT FROM сравнивает значения NULL-безопасно и без UNKNOWN:

AND u.name IS DISTINCT FROM 'Alice';


Если name уже Alice — условие ложно, и строка не обновляется вообще.

Это правило можно применить к любым колонкам:
AND u.email IS DISTINCT FROM EXCLUDED.email;


Паттерн остаётся тем же: обновление только при расхождении.

Проверьте сами на тесте:
CREATE TABLE users(id int PRIMARY KEY, name text);
INSERT INTO users VALUES (1, 'Bob');


Запустите 2 раза UPDATE сверху, второй раз таблица не изменится и ничего не запишет.

🔥 UPDATE должен менять только то, что отличается и SQL уже даёт для этого инструменты.

➡️ SQL Ready | #совет

Читать полностью…

SQL Ready | Базы Данных

Всех с наступающим 2026 годом! 🎄

Поставлю себе цель: набрать более 20 тысяч подписчиков ✍️

Читать полностью…

SQL Ready | Базы Данных

Знали, что NOT IN — главный источник тихих багов, если в подзапросе есть NULL?

Этот запрос выглядит логично, но опасен:

WHERE user_id NOT IN (SELECT id FROM users);


Если подзапрос вернёт хотя бы один NULL, условие станет UNKNOWN, и не вернётся ни одной строки.

Правильнее и безопаснее паттерн NOT EXISTS:
WHERE NOT EXISTS (SELECT 1 FROM users u WHERE u.id = orders.user_id);


Он корректно работает при NULL, не ломает логику и читается однозначно.

Хотите повторить? Проверьте разницу сами:
CREATE TABLE users(id int);
INSERT INTO users VALUES (1), (2), (NULL);


Теперь запустите:
SELECT 1 AS test WHERE 3 NOT IN (SELECT id FROM users);


Результат будет пустым.

🔥 А если переписать через NOT EXISTS, логика вернётся в норму.

➡️ SQL Ready | #совет

Читать полностью…

SQL Ready | Базы Данных

🖥 PostgreSQL: работа с массивами!

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

➡️ SQL Ready | #шпора

Читать полностью…

SQL Ready | Базы Данных

Оконные функции ROW_NUMBER() в SQL — нумерация строк для аналитики!

ROW_NUMBER() присваивает уникальный порядковый номер строкам внутри логического окна. Данные не объединяются в группы, строки остаются как есть — это ключевое отличие от GROUP BY.

Таблица:

payments(id, user_id, amount, created_at)


Нумеруем платежи каждого пользователя. Окно создаётся по user_id, а нумерация идёт по дате от старых к новым:
SELECT id, user_id, amount,
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at ASC) AS rn
FROM payments;


PARTITION BY разбивает данные на сегменты (в данном случае — по пользователю). ORDER BY внутри OVER() задаёт, в каком порядке будут присваиваться номера.

Чтобы найти последний платёж каждого пользователя, меняем порядок сортировки на DESC. Самая свежая запись получит номер 1 в своём окне:
WITH t AS (
SELECT id, user_id, amount,
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC) AS rn
FROM payments
)
SELECT * FROM t WHERE rn = 1;


Здесь используем CTE (WITH), чтобы сначала пронумеровать строки, а затем отфильтровать только нужный номер.

Следующий пример — логины пользователей:
auth_logs(id, user_id, ip, login_at)


Ищем первый вход каждого пользователя с каждого IP:
WITH t AS (
SELECT id, user_id, ip,
ROW_NUMBER() OVER (PARTITION BY user_id, ip ORDER BY login_at ASC) AS rn
FROM auth_logs
)
SELECT * FROM t WHERE rn = 1;


🔥 ROW_NUMBER() подходит, когда нужен номер строки в сегменте, важно выбрать первую/последнюю запись по логике сортировки,требуется топ-N по категориям или пользователям.

➡️ SQL Ready | #практика

Читать полностью…

SQL Ready | Базы Данных

☕️ Postgrespro — полная и актуальная документация по PostgreSQL!

SQL-синтаксис, индексы, транзакции, планировщик запросов, репликация, расширения и внутренняя архитектура. Здесь подробно описано не только как писать запросы, но и как база данных работает. Полезно, если используешь Postgres в продакшене или хочешь глубже разобраться в производительности.

📌 Оставляю ссылочку: postgrespro.ru

➡️ SQL Ready | #ресурс

Читать полностью…

SQL Ready | Базы Данных

☕️ StrataScratch — вопросы с собеседований!

Готовишься к собеседованиям по SQL или хочешь укрепить свои практические навыки? Этот сайт собрал самые важные вопросы, которые реально встречаются на интервью. Здесь есть и запросы на выборки, агрегаты и JOIN, и открытые задачи, где нужно не просто написать код, а объяснить логику решения.

📌 Оставляю ссылочку: stratascratch.com

➡️ SQL Ready | #ресурс

Читать полностью…

SQL Ready | Базы Данных

📂 Напоминалка по SQL Injection (SQLi)!

Например, простая инъекция вроде OR 1=1 может вернуть все данные из таблицы, а blind SQLi позволяет вытаскивать информацию даже тогда, когда приложение не показывает ошибки и результаты запросов.

На картинке — основные типы SQL-инъекций, которые важно знать при работе с базами данных и backend-логикой.

Сохрани, чтобы не забыть!

➡️ SQL Ready | #ресурс

Читать полностью…

SQL Ready | Базы Данных

NULL и NOT IN — тонкость SQL, приводящая к логическим ошибкам!

При использовании NOT IN в SQL можно получить логически неверный результат без ошибок выполнения. Причина — трёхзначная логика и наличие NULL в данных.

Представим таблицы:

customers(id)
orders(id, customer_id)


Нужно найти клиентов, у которых нет заказов.

Интуитивный вариант:
SELECT id
FROM customers
WHERE id NOT IN (
SELECT customer_id
FROM orders
);


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

Это происходит потому, что NOT IN сводится к серии сравнений, а любое сравнение с NULL возвращает неопределённый результат.

Попытка исправить ситуацию фильтрацией:
SELECT id
FROM customers
WHERE id NOT IN (
SELECT customer_id
FROM orders
WHERE customer_id IS NOT NULL
);


Формально запрос корректен, но требует постоянного контроля и легко ломается при изменении подзапроса.

Надёжный вариант — использовать NOT EXISTS:
SELECT c.id
FROM customers c
WHERE NOT EXISTS (
SELECT 1
FROM orders o
WHERE o.customer_id = c.id
);


NOT EXISTS корректно обрабатывает NULL и предназначен именно для проверок отсутствия связанных строк.

🔥 Используй NOT EXISTS для анти-джойнов и проверок отсутствия данных, а NOT IN — только при полном контроле результата подзапроса.

➡️ SQL Ready | #практика

Читать полностью…

SQL Ready | Базы Данных

📂 Напоминалка по масштабированию баз данных!

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

На картинке — 10 техник масштабирования БД, которые стоит держать под рукой при работе с высокими нагрузками.

Сохрани, чтобы не забыть!

➡️ SQL Ready | #ресурс

Читать полностью…

SQL Ready | Базы Данных

😎 SQL Style Guide — супер полезный репозиторий для освоения языка!

Практичный ресурс по написанию SQL: как оформлять SELECT, JOIN, CTE, подзапросы и имена таблиц, чтобы запросы были понятными, поддерживаемыми и удобными для работы в команде. Подходит для любых СУБД и реально упрощает работу и учебу.

Оставляю ссылочку: GitHub 📱


➡️ SQL Ready | #репозиторий

Читать полностью…

SQL Ready | Базы Данных

Как корректно сравнивать значения с NULL?

Обычное сравнение может сломаться, когда в данных появляется NULL. В SQL выражение:

email <> 'admin@example.com'


не вернёт строки с email IS NULL — результат будет UNKNOWN.

Для корректного сравнения используйте IS DISTINCT FROM:
email IS DISTINCT FROM 'admin@example.com'


Оно работает так, как ожидаешь:
NULL ≠ любое значение
NULL = NULL

Результат всегда TRUE или FALSE (без UNKNOWN).

То же самое для проверки изменений:
old_value IS DISTINCT FROM new_value


🔥 Это инструмент не для синтаксиса, а для корректности данных.

➡️ SQL Ready | #совет

Читать полностью…

SQL Ready | Базы Данных

📂 Напоминалка по структурам данных для экономии памяти и работы с большими данными!

Например, Bloom Filter позволяет быстро проверить, встречался ли элемент ранее, а HyperLogLog помогает оценить количество уникальных значений, не храня все данные целиком.

На картинке — 6 структур данных, которые стоит держать под рукой при проектировании backend-систем, аналитики и highload-сервисов.

Сохрани, чтобы не забыть!

➡️ SQL Ready | #ресурс

Читать полностью…

SQL Ready | Базы Данных

Префиксные индексы в MySQL — ускоряем поиск по длинным строкам!

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

Создадим таблицу с длинным текстовым атрибутом — типичный кейс, где полный индекс был бы слишком тяжёлым:

CREATE TABLE documents (
id INT PRIMARY KEY,
doc_key VARCHAR(500) NOT NULL
);


Добавим префиксный индекс. Индексируются только первые 20 символов:
CREATE INDEX idx_doc_key_prefix
ON documents (doc_key(20));


Если фильтровать данные по фиксированному началу строки, MySQL использует префиксный индекс:
SELECT id
FROM documents
WHERE doc_key LIKE 'INV-2024-%';


Важно: индекс применяется только если шаблон начинается без ведущего %. Например, LIKE '%2024%' уже не сможет его использовать.

Пример с email — если полная индексация не нужна:
CREATE INDEX idx_email_prefix
ON users (email(16));


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

➡️ SQL Ready | #практика

Читать полностью…

SQL Ready | Базы Данных

Почему Index Only Scan в PostgreSQL не всегда работает?

Если PostgreSQL не использует Index Only Scan, проблема часто не в запросе и не в самом индексе.

Index Only Scan работает только если страницы помечены как видимые в visibility map. Если этого нет PostgreSQL всё равно идёт в таблицу.

Проверьте план выполнения:

EXPLAIN ANALYZE
SELECT id FROM orders WHERE status = 'pending';


Если видите Index Scan, а не Index Only Scan, одна из частых причин в том, что visibility map не заполнена (при наличии подходящего covering index).

Исправляется простой командой:
VACUUM (ANALYZE) orders;


🔥 VACUUM помечает страницы как all-visible, и PostgreSQL может перестать читать таблицу.

➡️ SQL Ready | #совет

Читать полностью…

SQL Ready | Базы Данных

Партицирование в оконных функциях — когда нужно работать с топами внутри групп!

Оконные функции в SQL — стандартный инструмент аналитики. Один из частых кейсов: найти «лучшие» записи внутри каждой группы, не сворачивая таблицу и не теряя строки.

Представим таблицу:

products(id, category, price)


Нужно выбрать самый дорогой товар в каждой категории, но при этом видеть все колонки строки:
SELECT *
FROM (
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY category ORDER BY price DESC, id DESC) AS rn
FROM products
) t
WHERE rn = 1;


PARTITION BY формирует независимые окна для каждой категории.
ROW_NUMBER() нумерует строки внутри каждой партиции, а не по всей таблице.

Если нужны не только первые, а, например, топ-3 в каждой категории:
SELECT id, category, price
FROM (
SELECT
id, category, price,
ROW_NUMBER() OVER (PARTITION BY category ORDER BY price DESC, id DESC) AS rn
FROM products
) t
WHERE rn <= 3
ORDER BY category, price DESC;


Тут важно: без PARTITION BY запрос взял бы топ-3 по всей таблице, а не по категориям.

Оконные функции не требуют GROUP BY, потому что не агрегируют (не схлопывают) строки, а дополняют их аналитическими метками.

Ещё полезный трюк — находить максимум в группе без GROUP BY и JOIN, через коррелированный подзапрос:
SELECT *
FROM products
WHERE price = (
SELECT MAX(price)
FROM products p2
WHERE p2.category = products.category
);


Этот запрос не агрегирует основную таблицу и вернёт несколько строк, если в категории есть товары с одинаковой максимальной ценой (ties).

🔥 Используйте PARTITION BY в оконных функциях, когда логика должна применяться внутри каждой группы независимо, а строки нужно сохранить целиком.

➡️ SQL Ready | #практика

Читать полностью…

SQL Ready | Базы Данных

📂 Напоминалка про Modem vs Router!

Модем отвечает за подключение к провайдеру и получение публичного IP, а роутер управляет локальной сетью: маршрутизирует трафик, выполняет NAT и раздаёт адреса устройствам.

На картинке — ключевые отличия и базовая схема работы.

Сохрани, чтобы не забыть!

➡️ SQL Ready | #ресурс

Читать полностью…

SQL Ready | Базы Данных

❤️ Red Blob Games — полезный сайт для визуального понимания алгоритмов!

Если тебе сложно понять алгоритмы по учебникам — этот ресурс решает проблему. Автор объясняет графы, геометрию, деревья, меши из геймдева через понятные визуализации и интерактивы. Сложные идеи здесь раскладываются на простые шаги.

📌 Оставляю ссылочку: redblobgames.com

➡️ SQL Ready | #ресурс

Читать полностью…

SQL Ready | Базы Данных

Генерация фейковых данных для тестов с мгновенной вставкой в любую таблицу!

SQL понимает VALUES как виртуальную таблицу, поэтому можно писать много строк прямо в INSERT:

INSERT INTO users (id, email, name) VALUES
(1, 'alice@mail.com', 'Alice'),
(2, 'bob@mail.com', 'Bob'),
(3, 'carol@mail.com', 'Carol');


Это работает мгновенно, просто читается и не создаёт лишних объектов в базе.

Хотите протестировать в своем проекте? Создайте таблицу и вставьте строки выше:
CREATE TABLE users (
id int PRIMARY KEY,
email text,
name text
);


🔥 Пригодится, когда нужно быстро заполнить таблицу для проверки логики.

➡️ SQL Ready | #совет

Читать полностью…

SQL Ready | Базы Данных

Развернуть PostgreSQL в MWS Cloud Platform ⬜️ — быстрее, чем вспомнить пароль от pgAdmin. И точно быстрее, чем объяснить DevOps'у, зачем ещё одна база.

Всего несколько минут и у вас:

⏺️готовая база на сетевых или локальных дисках
⏺️постоянный primary endpoint
⏺️безопасное подключение через Private Link
⏺️автоматические бэкапы и обслуживания по твоему расписанию


🎄🎁 И грант до 10 000 ₽ на запуск — чтобы точно не пришлось вспоминать, как настраивать failover вручную.

➡️Развернуть кластер

Читать полностью…

SQL Ready | Базы Данных

☕️ На Хабре вышла подробная статья про автоматизацию развертывания PostgreSQL-кластеров в изолированной инфраструктуре.

В этой статье:
• Показан реальный подход к автоматическому развёртыванию PostgreSQL в закрытом контуре;
• Разбирается поддержка нескольких ОС, версий СУБД и схем отказоустойчивости;
• Описана автоматическая проверка соответствия требованиям архитектуры;
• Приведён практический кейс внедрения, рассчитанный на эксплуатацию в крупных корпоративных системах.

🔊 Продолжайте читать на Habr!


➡️ SQL Ready | #статья

Читать полностью…

SQL Ready | Базы Данных

Как понять, что PostgreSQL работает на устаревшей статистике?

PostgreSQL хранит информацию о последнем сборе статистики:

SELECT relname,
last_analyze,
last_autoanalyze,
n_live_tup
FROM pg_stat_user_tables;


last_analyze — когда статистика обновлялась вручную,
last_autoanalyze — когда это делал autovacuum.

Чтобы быстро найти проблемные таблицы, отсортируем по размеру:
SELECT relname,
n_live_tup,
last_analyze,
last_autoanalyze
FROM pg_stat_user_tables
ORDER BY n_live_tup DESC;


Большая таблица + старый last_analyze — оптимизатор работает вслепую.

В таком случае достаточно обновить статистику:
ANALYZE;


Или точечно, для одной таблицы:
ANALYZE orders;


🔥 Это помогает объяснить внезапную деградацию запросов и понять, почему индекс игнорируется.

➡️ SQL Ready | #совет

Читать полностью…

SQL Ready | Базы Данных

🖥 Ищем клиентов без единого заказа — полезный приём для выявления неактивных пользователей и повышения конверсии!

Цель — найти всех зарегистрированных пользователей, которые так и не оформили ни одного заказа. Это поможет вернуться к ним с акциями или напоминаниями.

Основные моменты:

• LEFT JOIN — соединяем таблицы, чтобы сохранить всех клиентов, даже тех, у кого нет заказов.

• WHERE o,id IS NULL — отбираем только тех, для кого заказов не найдено.

• SELECT — выводим имя, email и дату регистрации.


➡ SQL Ready | #задача

Читать полностью…

SQL Ready | Базы Данных

Условный UPSERT: как не обновлять строки без изменений?

Обычный UPSERT обновляет строку всегда, даже если данные не изменились — это лишние блокировки, WAL и autovacuum.

PostgreSQL позволяет сделать условный UPDATE прямо в ON CONFLICT:

ON CONFLICT (id) DO UPDATE
...
WHERE users.email IS DISTINCT FROM EXCLUDED.email
OR users.name IS DISTINCT FROM EXCLUDED.name;


Если данные совпадают — UPDATE не выполняется вообще.

EXCLUDED — это “новая” версия строки, users.* — текущая версия в таблице.
users.col IS DISTINCT FROM EXCLUDED.col


🔥 Корректно работает даже с NULL и не попадает в ловушки трёхзначной логики.

➡️ SQL Ready | #совет

Читать полностью…

SQL Ready | Базы Данных

FILTER в агрегатных функциях PostgreSQL!

В аналитических запросах часто нужно посчитать несколько показателей из одной таблицы. В PostgreSQL для этого есть FILTER, позволяющий задавать условия отдельно для каждой агрегатной функции, не влияя на весь запрос.

Представим таблицу заказов:

orders(id, customer_id, amount, status)


Посчитаем общее количество заказов и количество завершённых:
SELECT
COUNT(*) AS total_orders,
COUNT(*) FILTER (WHERE status = 'completed') AS completed_orders
FROM orders;


FILTER применяется непосредственно к агрегатной функции и ограничивает только те строки, которые участвуют в её расчёте.

Добавим несколько метрик в одном запросе:
SELECT
COUNT(*) AS total_orders,
COUNT(*) FILTER (WHERE status = 'completed') AS completed_orders,
COUNT(*) FILTER (WHERE status = 'canceled') AS canceled_orders,
SUM(amount) FILTER (WHERE status = 'completed') AS completed_amount
FROM orders;


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

FILTER можно использовать с любыми агрегатами:
AVG(amount) FILTER (WHERE status = 'completed')
MAX(amount) FILTER (WHERE status = 'completed')
MIN(amount) FILTER (WHERE status = 'completed')


🔥 Важно помнить: FILTER работает только с агрегатными функциями и применяется внутри SELECT, дополняя, а не заменяя WHERE и GROUP BY.

➡️ SQL Ready | #практика

Читать полностью…

SQL Ready | Базы Данных

🖥 GROUPING SETS — несколько отчётов в одном запросе!

В аналитике часто нужно считать данные сразу на нескольких уровнях: детализация, промежуточные итоги и общий результат. GROUPING SETS позволяет описать эту структуру напрямую.

Сегодня в гайде:

• Как считать несколько уровней агрегации за один проход по данным;

• Как отличать строки-итоги от обычных данных;

• Почему такой подход проще поддерживать и масштабировать.


Приём, который делает отчёты чище, быстрее и предсказуемее при росте требований.

➡️ SQL Ready | #гайд

Читать полностью…

SQL Ready | Базы Данных

DISTINCT vs GROUP BY — выбираем правильный инструмент для удаления дублей!

В SQL часто нужно избавиться от повторяющихся строк: уникальные пользователи, товары, категории. Для этого используют DISTINCT и GROUP BY. Результат может выглядеть одинаково, но назначение и смысл у этих конструкций разные.

Представим таблицу заказов:

orders(id, customer_id, product_id)


Найдём всех уникальных клиентов, которые делали заказы:
SELECT DISTINCT customer_id
FROM orders;


DISTINCT удаляет дубликаты по всему набору выбранных колонок в результирующем наборе — без группировок и агрегаций.

Сделаем то же самое через GROUP BY:
SELECT customer_id
FROM orders
GROUP BY customer_id;


Результат будет тем же, но семантически запрос другой: явно группируем строки по customer_id. В простых случаях оптимизатор часто строит одинаковый план, но логика запроса уже «про группы».

GROUP BY становится необходимым, когда появляются агрегаты.

Посчитаем количество заказов на каждого клиента:
SELECT customer_id, COUNT(*) AS orders_count
FROM orders
GROUP BY customer_id;


В этом запросе GROUP BY обязателен, потому что мы одновременно выбираем агрегат (COUNT(*)) и неагрегированное поле (customer_id).

Частая ошибка — смешивать DISTINCT и агрегаты без GROUP BY:
SELECT DISTINCT customer_id, COUNT(*)
FROM orders;


Такой запрос в стандартном SQL некорректен: неагрегированные поля должны присутствовать в GROUP BY. В зависимости от СУБД и режима он либо не выполнится, либо вернёт неопределённый результат.

Корректный вариант:
SELECT customer_id, COUNT(*) AS orders_count
FROM orders
GROUP BY customer_id;


🔥 Используй DISTINCT для простого удаления дублей, а GROUP BY — когда нужна агрегация, расчёты по группам или HAVING.

➡️ SQL Ready | #практика

Читать полностью…

SQL Ready | Базы Данных

Как избежать блокировок таблиц с помощью advisory locks в PostgreSQL!

Иногда нужно гарантировать, что только один процесс выполняет критическую секцию, но при этом не хочется блокировать таблицы и строки.

Для этого PostgreSQL предоставляет advisory locks — логические блокировки, не привязанные к таблицам или строкам.

SELECT pg_advisory_xact_lock(42);


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

Ключ — это просто число. Можно использовать user_id, order_id, хеш или tenant_id.
SELECT pg_advisory_xact_lock(user_id);


🔥 Это превращает PostgreSQL в механизм распределённой синхронизации. После COMMIT или ROLLBACK блокировка снимается автоматически.

➡️ SQL Ready | #совет

Читать полностью…

SQL Ready | Базы Данных

❤️ SQL-101 — руководство, помогающее освоить язык и укрепить базу тем, кто уже работает с БД.

В этом ресурсе собраны ключевые темы, которые нужны в работе: базовые запросы, фильтрация, JOIN, группировки, подзапросы, индексы, транзакции и основы оптимизации. Всё объяснено простым языком и дополнено примерами с упражнениями.

Оставляю ссылочку: Github 📱


➡️ SQL Ready | #репозиторий

Читать полностью…

SQL Ready | Базы Данных

⚡️ ВАЙБ-КОДИНГ теперь в Telegram!

Ребята сделали крутейший канал, где на наглядных примерах и понятном языке рассказывают как войти в новую эру разработки с ИИ, делятся полезными фишками и инструментами

Подписывайтесь, нас уже 10 тысяч: @vibecoding_tg

Читать полностью…
Subscribe to a channel