SQL Ready | Базы Данных
15.5K subscribers
1.25K photos
76 videos
2 files
625 links
Авторский канал про Базы Данных и SQL
Ресурсы, гайды, задачи, шпаргалки.
Информация ежедневно пополняется!

Автор: @energy_it

РКН: https://clck.ru/3QREBc

Реклама на бирже: https://telega.in/c/sql_ready
Download Telegram
📂 Напоминалка: как выполняется SQL-запрос в базе данных!

Например, когда отправляешь SELECT, UPDATE или другой SQL, СУБД сначала обрабатывает его внутренне, а уже потом выполняет.

На схеме — путь запроса: приём — разбор (парсер) — оптимизация — выполнение — доступ к данным — буферы, транзакции и блокировки — чтение/запись (память или диск).

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

➡️ SQL Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
16🔥9👍8
Анти-JOIN — как корректно выбирать строки без связанных записей!

Частая задача в SQL — найти строки, для которых отсутствуют связанные записи в другой таблице. Это классический anti-join. Например, нужно получить клиентов, которые ни разу не сделали заказ.

Таблицы:
customers(id, name)
orders(id, customer_id, created_at)


LEFT JOIN + IS NULL:
SELECT c.id, c.name
FROM customers c
LEFT JOIN orders o
ON o.customer_id = c.id
WHERE o.id IS NULL;


LEFT JOIN возвращает всех клиентов. Если совпадений нет, поля из orders становятся NULL, и фильтр оставляет только клиентов без заказов.

Проверять нужно гарантированно NOT NULL колонку правой таблицы (обычно PK, например o.id), иначе возможны логические ошибки.

NOT EXISTS (предпочтительно):
SELECT c.id, c.name
FROM customers c
WHERE NOT EXISTS (
SELECT 1
FROM orders o
WHERE o.customer_id = c.id
);


Запрос напрямую выражает отсутствие строк. В простом случае он эквивалентен LEFT JOIN, но лучше отражает намерение, устойчив к NULL в подзапросе и часто даёт более предсказуемый план при усложнении условий.

Почему не NOT IN:
SELECT c.id, c.name
FROM customers c
WHERE c.id NOT IN (
SELECT customer_id
FROM orders
);


Если подзапрос возвращает хотя бы один NULL, результат может стать пустым из-за трёхзначной логики SQL. Использовать этот вариант безопасно только при гарантированном NOT NULL в orders.customer_id.

Нюанс с условиями в JOIN:
SELECT c.id
FROM customers c
LEFT JOIN orders o
ON o.customer_id = c.id
AND o.created_at >= DATE '2026-01-01'
WHERE o.id IS NULL;


Этот запрос ищет клиентов без заказов после указанной даты.

Если требуется найти клиентов вообще без заказов, добавлять условия в JOIN нельзя — это меняет бизнес-смысл. В таком случае корректнее использовать NOT EXISTS.

🔥 Минимально необходим индекс orders(customer_id). При частой фильтрации по дате логичен составной индекс (customer_id, created_at).

➡️ SQL Ready | #практика
Please open Telegram to view this post
VIEW IN TELEGRAM
16👍10🔥8
This media is not supported in your browser
VIEW IN TELEGRAM
❤️ Stepik SQL — практический курс с примерами запросов и упражнениями!

Этот репозиторий содержит структурированные задания, схемы баз данных и примеры SQL-запросов из курса на Stepik. Материалы охватывают работу с таблицами, фильтрацию, JOIN, группировки, агрегаты и другие ключевые темы языка, с акцентом на практику. Отличный ресурс для тех, кто хочет закрепить теорию через задачи.

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


➡️ SQL Ready | #репозиторий
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1712👍10
Почему OFFSET может ломать производительность пагинации?

OFFSET выглядит удобно для страниц, но база всё равно должна просканировать и пропустить все предыдущие строки. Поэтому чем дальше страница, тем медленнее запрос:
OFFSET 100000


Даже если вы не видите эти строки, СУБД всё равно читает их из индекса или таблицы, поэтому время выполнения растёт вместе с OFFSET:
SELECT *
FROM orders
WHERE id > :last_seen_id
ORDER BY id
LIMIT 10;


Эффективнее использовать keyset-pagination — продолжать выборку от последнего значения ключа, а не пропускать строки:
WHERE id > :last_seen_id


:last_seen_id — это id последней строки предыдущей страницы.

Такой запрос использует индекс напрямую (если id индексирован, обычно это PK), поэтому работает одинаково быстро на первой и на миллионной странице.

Если сортировка не уникальная (например created_at), добавляйте tie-breaker:
ORDER BY created_at, id
WHERE (created_at, id) > (:last_seen_created_at, :last_seen_id)


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

➡️ SQL Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1715🔥8
☕️ Полезную статью нашёл на Хабре: «Инженерная история: добавляем 3-ю СУБД в карточный процессинг»!

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


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


➡️ SQL Ready | #статья
Please open Telegram to view this post
VIEW IN TELEGRAM
11👍8🔥8
Агрегирование по группам с оконными функциями!

Когда нужно посчитать агрегаты по группе и при этом сохранить исходные строки, многие делают подзапрос с GROUP BY и потом джойнят его обратно:
SUM(o.total) OVER (PARTITION BY o.user_id)


Оконная функция считает агрегат по группе, но не схлопывает строки, поэтому не нужен ни GROUP BY, ни повторное соединение:
AVG(o.total) OVER (PARTITION BY o.user_id)


Можно получить любые метрики по той же группе в одном проходе по данным:
COUNT(*) OVER (PARTITION BY o.user_id)


Особенно полезно для аналитики, отчётов, флагов “у пользователя больше N заказов” и подобных задач без лишних подзапросов.

🔥 Один из самых эффективных способов упростить сложные запросы.

➡️ SQL Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14🔥9🤝8
This media is not supported in your browser
VIEW IN TELEGRAM
💡 Rows — умные таблицы с встроенным ИИ!

Сервис для работы с данными в формате таблиц (аналог Excel / Google Sheets) с интегрированными AI-инструментами. Позволяет анализировать данные, создавать формулы по описанию и подтягивать информацию из интернета и API. Подходит для аналитики, автоматизации и обработки больших массивов данных.

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

➡️ SQL Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
👍148🔥8🤝1
🖥 Анализ средней длительности сессий пользователей по устройствам!

В этой задаче напишем SQL-запрос, который поможет вычислить среднее время сессии для пользователей на разных устройствах за последние 30 дней.

Что делаем:
Фильтруем сессии по времени и устройствам.

Считаем длительность каждой сессии.

Группируем и находим среднее время по типам устройств.


Такой анализ помогает понять, в каких моментах сфокусироваться на улучшении UX и маркетинговых кампаниях.

➡️ SQL Ready | #задача
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥15👍108🤝1
📂 Напоминалка по SQL JOIN!

Например, LEFT JOIN позволяет получить все строки из левой таблицы, даже если соответствующих записей в правой таблице нет, а FULL OUTER JOIN возвращает все строки из обеих таблиц, заполняя отсутствующие значения NULL.

На изображении показаны 4 основных типа SQL JOIN, которые чаще всего используются при объединении данных из нескольких таблиц.

📌 Сохрани, чтобы не потерять!

➡️ SQL Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14🔥10🤝83
Атомарное перемещение данных между таблицами!

Иногда нужно архивировать старые данные: удалить их из основной таблицы и одновременно сохранить в архиве. Многие делают это двумя запросами (INSERT => DELETE):
DELETE FROM orders
RETURNING *


RETURNING позволяет сразу вернуть удалённые строки как результирующий набор, не выполняя повторный SELECT.
WITH moved AS (...)


CTE превращает результат удаления во временный набор данных, который можно использовать в следующей операции:
INSERT INTO orders_archive
SELECT * FROM moved;


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

🔥 Полезный паттерн для архивирования, миграций, дедупликации и batch-операций над большими таблицами.

➡️ SQL Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
👍15🤝14🔥101
This media is not supported in your browser
VIEW IN TELEGRAM
❤️ RusauSQL — подробный учебник по SQL для тестировщиков и разработчиков!

Это большой гайд по SQL, где последовательно объясняется, как работать с базами данных и писать запросы. На сайте разбираются фундаментальные вещи: что такое SQL, как устроены таблицы и базы данных, как выполнять запросы, проверять данные и взаимодействовать с сервером через язык запросов. Отличный ресурс, если хочешь разобраться в SQL с практической стороны и понять работу баз данных.

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

➡️ SQL Ready | #сайт
Please open Telegram to view this post
VIEW IN TELEGRAM
👍128🔥7🤝2
SEMI JOIN через EXISTS — как корректно проверять наличие связанных строк!

Частая задача в SQL — выбрать строки из одной таблицы, если в другой таблице существует хотя бы одна связанная запись.

Таблицы:
customers(id, name)
orders(id, customer_id, created_at)


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

Попытка через JOIN:
SELECT
c.id,
c.name
FROM customers c
JOIN orders o
ON o.customer_id = c.id;


Запрос вернёт клиентов, но если у клиента несколько заказов, он появится в результате несколько раз.

Чтобы убрать дубликаты, часто добавляют DISTINCT:
SELECT DISTINCT
c.id,
c.name
FROM customers c
JOIN orders o
ON o.customer_id = c.id;


Результат будет корректным, однако такой запрос выражает задачу менее точно: JOIN формирует строки для каждого совпадения, а DISTINCT затем устраняет повторяющиеся значения.

В подобных задачах фактически требуется semi join — вернуть строку из customers, если существует хотя бы одна связанная строка в orders.

В большинстве СУБД это обычно выражают через EXISTS:
SELECT
c.id,
c.name
FROM customers c
WHERE EXISTS (
SELECT 1
FROM orders o
WHERE o.customer_id = c.id
);


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

Оптимизатор часто может завершить проверку сразу после нахождения первого совпадения, поэтому EXISTS является естественным инструментом для проверки существования строк.

Практический пример — клиенты, которые делали заказы после определённой даты:
SELECT
c.id,
c.name
FROM customers c
WHERE EXISTS (
SELECT 1
FROM orders o
WHERE o.customer_id = c.id
AND o.created_at >= '2025-01-01'
);


EXISTS не возвращает данные из подзапроса — он только проверяет факт существования строки.

Поэтому внутри обычно пишут:
SELECT 1


или:
SELECT *


В контексте EXISTS список выбираемых выражений не влияет на результат.

🔥 EXISTS — стандартный и выразительный способ реализовать semi join. Он точно отражает намерение запроса и часто позволяет оптимизатору построить более эффективный план выполнения.

➡️ SQL Ready | #практика
Please open Telegram to view this post
VIEW IN TELEGRAM
👍13🔥96🤝6
🐱 Крутейшая статья вышла на Хабре: «Научил ИИ-агента помнить важное и забывать лишнее в SQLite»!

В этой статье:
• Разбирается, почему классический RAG плохо подходит для долгоживущих AI-агентов;
• Показана архитектура когнитивной памяти на SQLite: граф узлов и рёбер, сущности, гибридный поиск и механизм разрешения конфликтов знаний;
• Объясняется, как реализовать память агента с механизмом забывания и консолидацией фактов.


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


➡️ SQL Ready | #статья
Please open Telegram to view this post
VIEW IN TELEGRAM
12👍7🔥6🤝3
Передавайте списки значений через массивы вместо длинных IN!

Когда из приложения нужно передать список id в SQL, многие пишут IN (... , ... , ...), что увеличивает текст SQL и усложняет работу с prepared statements:
WHERE id IN (1,2,3,4,5,6,7)


PostgreSQL умеет принимать массив и сравнивать его через ANY — это короче и позволяет избежать генерации динамического SQL:
WHERE id = ANY($1)


Приложение просто передаёт массив параметром, а текст запроса остаётся неизменным независимо от длины списка:
SELECT *
FROM users
WHERE email = ANY($1);


На практике полезно явно указывать тип массива ($1::bigint[]), а также помнить, что NULL внутри массива влияет на результат сравнения.

🔥 Этот паттерн особенно удобен для batch-запросов, API-фильтров и массовых выборок, где количество значений может сильно меняться.

➡️ SQL Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14🔥1210
👍10🔥6🤝41👎1
Что же выведет консоль?
Anonymous Quiz
35%
A
31%
B
13%
C
21%
D
👍12🔥7🤝72👎2
VACUUM — чистим таблицу и ускоряем работу базы!

В большинстве СУБД удалённые или обновлённые строки не удаляются мгновенно, а остаются как «мертвые» записи. Со временем это замедляет запросы и увеличивает размер таблиц — нужна очистка.

Сначала удаляем устаревшие записи из таблицы:
DELETE FROM sessions WHERE expired = true;


Однако физически строки всё ещё занимают место.

Чтобы освободить пространство и обновить статистику, запускаем:
VACUUM ANALYZE sessions;


Для диагностики можно получить подробный отчёт о процессе очистки:
VACUUM VERBOSE sessions;


🔥 Регулярный VACUUM поддерживает производительность и точность планов запросов.

➡️ SQL Ready | #практика
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥159👍9