sql_ready | Unsorted

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

7786

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

Subscribe to a channel

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

🐱 Крутейшая статья вышла на Хабре: «Научил ИИ-агента помнить важное и забывать лишнее в SQLite»!

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


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


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

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

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

❤️ RusauSQL — подробный учебник по SQL для тестировщиков и разработчиков!

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

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

➡️ SQL Ready | #сайт

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

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

📂 Напоминалка по SQL JOIN!

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

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

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

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

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

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

💡 Rows — умные таблицы с встроенным ИИ!

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

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

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

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

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

☕️ Полезную статью нашёл на Хабре: «Инженерная история: добавляем 3-ю СУБД в карточный процессинг»!

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


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


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

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

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

❤️ Stepik SQL — практический курс с примерами запросов и упражнениями!

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

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


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

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

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

📂 Напоминалка: как выполняется SQL-запрос в базе данных!

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

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

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

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

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

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

🖥 Oracle: работа с датой и временем!

Основные функции и приёмы работы с календарными значениями в Oracle: получение текущих даты и времени на стороне БД, вычисление интервалов, смещение по месяцам, усечение до нужной гранулярности, извлечение компонентов даты, а также корректное преобразование строк в DATE и TIMESTAMP. Полезно для отчётов, аналитики, планирования, ETL-процессов и любой логики, связанной с датами и временем.

➡️ SQL Ready | #шпора

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

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

Проверка совпадений сразу по нескольким полям одной операцией!

Когда нужно сравнить строки по составному ключу, многие пишут условия с AND, которые хуже читаются и сложнее поддерживаются:

u.email = b.email AND u.phone = b.phone


SQL умеет сравнивать кортежи целиком (row value), поэтому несколько колонок можно проверить одним выражением:
(email, phone)


Поддержка зависит от СУБД. Учитывайте поведение NULL и наличие составных индексов, оптимизация не всегда идентична AND.

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

➡️ SQL Ready | #совет

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

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

✍️ Bilabon — задачи и теория по MySQL!

Это шпаргалка с ключевой теорией и упражнениями: управление БД и таблицами, типы данных, индексы, транзакции и оптимизация запросов. Также приведены практические задачи, позволяющие закрепить знания на примерах реальных предметных областей.

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


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

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

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

📂 Напоминалка по SQL — базовые команды и порядок выполнения запроса!

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

На картинке — основные операторы SQL, типы JOIN’ов и правильный порядок выполнения запроса внутри СУБД.

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

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

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

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

💡 SQL Fiddle — онлайн платформа для тестирования SQL без установки БД!

Сервис позволяет писать, запускать и проверять SQL-запросы прямо в браузере. Можно создать схему базы, заполнить тестовыми данными и сразу увидеть результат выполнения, идеально для экспериментов, обучения и разбора задач. Поддерживает разные СУБД (MySQL, PostgreSQL, Oracle, SQL Server и др.), поэтому удобно сравнивать поведение запросов.

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

➡️ SQL Ready | #сайт

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

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

TRUNC(date) — как правильно обрезать дату в Oracle без потери индекса!

В Oracle тип DATE хранит дату и время одновременно. Частая задача — сравнить только по дате, игнорируя время. Многие используют TRUNC(date), но это может сильно ухудшить производительность.

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

orders(id, created_at DATE)


Нужно выбрать заказы за 18 февраля 2026.

Интуитивный вариант:
SELECT *
FROM orders
WHERE TRUNC(created_at) = DATE '2026-02-18';


Логически верно, но есть проблема.

Функция применяется к колонке — поэтому обычный индекс по created_at в основном не используется, и Oracle вынужден вычислять TRUNC для множества строк, что часто приводит к полному сканированию.

Правильный способ — диапазон по дате:
SELECT *
FROM orders
WHERE created_at >= DATE '2026-02-18'
AND created_at < DATE '2026-02-19';


Теперь условие индекс-дружелюбное (sargable) — оптимизатор может использовать обычный индекс по created_at.

Этот подход корректно работает независимо от времени в поле.

TRUNC(date) полезен в SELECT или GROUP BY, когда фильтрация по индексу не критична:
SELECT
TRUNC(created_at) AS day,
COUNT(*) AS orders_count
FROM orders
GROUP BY TRUNC(created_at)
ORDER BY day;


Если всё же нужно часто фильтровать по TRUNC(created_at), можно создать функциональный индекс:
CREATE INDEX idx_orders_created_day
ON orders (TRUNC(created_at));


После этого запрос с TRUNC сможет использовать индекс.

Функция TRUNC умеет обрезать дату до разных уровней:
SELECT
TRUNC(SYSDATE) AS day,
TRUNC(SYSDATE, 'MM') AS month_start,
TRUNC(SYSDATE, 'YYYY') AS year_start
FROM dual;


Поддерживаются форматы: DD (день), MM (месяц), YYYY (год), IW (ISO-неделя) и др.

DATE хранит дату со временем, поэтому = DATE '2026-02-18' не находит все записи за день; функции над колонками мешают индексам.

🔥 Для фильтрации по дате используйте диапазоны, а TRUNC — для отображения, агрегации или вместе с функциональным индексом.

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

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

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

Проверка наличия связанных данных!

Когда нужно проверить существование связанных строк, обычный JOIN часто увеличивает результат или заставляет делать DISTINCT, что бьёт по производительности:

SELECT *
FROM orders o
WHERE EXISTS (
SELECT 1 FROM payments p
WHERE p.order_id = o.id
);


EXISTS останавливается на первой найденной строке и не создаёт дубликатов, поэтому обычно работает быстрее и предсказуемее:
SELECT *
FROM orders o
WHERE NOT EXISTS (
SELECT 1 FROM payments p
WHERE p.order_id = o.id
);


NOT EXISTS - наиболее корректный способ найти записи без соответствующих связанных данных.

В отличие от NOT IN он корректнее работает при наличии NULL:
SELECT *
FROM orders o
LEFT JOIN payments p ON p.order_id = o.id
WHERE p.order_id IS NULL;


🔥 LEFT JOIN + IS NULL делает то же самое логически, но чаще требует больше работы оптимизатора и может уступать по производительности на больших объёмах.

➡️ SQL Ready | #совет

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

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

LATERAL — коррелированные подзапросы!

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

Обычно это делают через оконки, CTE или GROUP BY + JOIN, что сложно читать и часто ломается при расширении логики:

LEFT JOIN LATERAL (...)


Выполняет подзапрос для каждой строки слева, подставляя её значения.
ORDER BY created_at DESC
LIMIT 1


Гарантирует, что ты берёшь именно нужную запись:
ON true


Потому что связь уже внутри подзапроса, JOIN тут формальность.

🔥 Это отличный инструмент, когда нужна одна зависимая строка на родителя

➡️ SQL Ready | #совет

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

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

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 | #практика

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

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

Атомарное перемещение данных между таблицами!

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

DELETE FROM orders
RETURNING *


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


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


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

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

➡️ SQL Ready | #совет

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

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

🖥 Анализ средней длительности сессий пользователей по устройствам!

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

Что делаем:

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

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

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


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

➡️ SQL Ready | #задача

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

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

Агрегирование по группам с оконными функциями!

Когда нужно посчитать агрегаты по группе и при этом сохранить исходные строки, многие делают подзапрос с 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 | #совет

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

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

Почему 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 | #совет

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

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

Анти-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 | #практика

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

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

❤️ Мега интересную статью нашёл на Хабре: «DOOMQL: DOOM-подобный многопользовательский шутер на чистом SQL»!

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


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


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

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

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

Дедупликация с приоритетом, оставляем лучшую строку!

Когда есть дубликаты по ключу (например, email), нужно сохранить запись с максимальным приоритетом — подтверждённую или самую новую.

Таблица: users(id, email, is_verified, created_at)

Сначала можно увидеть, какая строка будет считаться главной. Нумеруем строки внутри каждого email по приоритету:

SELECT id, email,
       ROW_NUMBER() OVER (
         PARTITION BY email
         ORDER BY is_verified DESC, created_at DESC
       ) rn
FROM users;


Оконная функция делит строки по email и присваивает номер согласно приоритету, где подтверждённые и более новые записи идут первыми.

Оставим только одну запись на email — ту, у которой rn = 1:
SELECT *
FROM (
  SELECT *,
         ROW_NUMBER() OVER (
           PARTITION BY email
           ORDER BY is_verified DESC, created_at DESC
         ) rn
  FROM users
) t
WHERE rn = 1;


Все остальные строки в каждой группе автоматически становятся дублями.

В PostgreSQL можно сделать то же самое короче:
SELECT DISTINCT ON (email)
       id, email, is_verified, created_at
FROM users
ORDER BY email, is_verified DESC, created_at DESC;


Здесь ORDER BY фактически задаёт правило выбора победителя внутри каждой группы. СУБД оставит одну строку на email согласно приоритету сортировки.

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

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

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

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

SYSDATE в условиях — почему = SYSDATE почти никогда не то, что нужно!

В Oracle SYSDATE возвращает текущие дату и время с точностью до секунды. Частая ошибка — пытаться выбрать записи «за сегодня» через прямое сравнение.

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

orders(id, created_at DATE)


Интуитивный вариант:
SELECT *
FROM orders
WHERE created_at = SYSDATE;


Логически кажется правильным — взять записи за текущий день. Но условие почти никогда не выполнится.

Тип DATE в Oracle хранит и дату, и время (до секунды). Чтобы совпадение произошло, created_at должен быть ровно равен текущему моменту до секунды. На практике такой запрос почти всегда возвращает пустой результат.

Попытка исправить через TRUNC:
SELECT *
FROM orders
WHERE TRUNC(created_at) = TRUNC(SYSDATE);


Работает корректно, но есть нюанс — функция применяется к колонке, поэтому обычный индекс по created_at не используется (если только нет function-based index на TRUNC(created_at)).

Правильный способ — диапазон по дате:
SELECT *
FROM orders
WHERE created_at >= TRUNC(SYSDATE)
  AND created_at <  TRUNC(SYSDATE) + 1;


Такое условие индекс-дружелюбное (sargable), и оптимизатор может использовать индекс. Запрос выбирает все записи за текущие сутки независимо от времени (по часовому поясу сессии/сервера БД).

Аналогично можно искать за любой день:
WHERE created_at >= DATE '2026-02-18'
  AND created_at <  DATE '2026-02-19'


Поскольку DATE хранит время, прямое равенство по дате почти никогда не подходит для выборки «за день».

🔥 Для выборки за день используйте полуоткрытый диапазон, а не сравнение с SYSDATE.

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

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

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

🖥 Объединяем пересекающиеся интервалы автоматически!

В реальных данных интервалы часто перекрываются или соприкасаются, из-за чего сложно понять фактические периоды непрерывной активности или занятости.

Сегодня в задаче:

Определим, насколько далеко тянется текущая занятость, отслеживая максимальный конец интервалов;

Найдём точки разрыва, где начинается новый независимый блок времени;

Объединим пересечения в итоговые непрерывные диапазоны.


Этот приём используют для анализа сессий, расписаний, бронирований и любых временных событий, где важно видеть реальные интервалы.

➡️ SQL Ready | #задача

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

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

Вычисление временных интервалов между датами!

PostgreSQL умеет возвращать полноценный интервал одной операцией:

now() - created_at


Если нужно получить возраст записи в читаемом виде (годы-месяцы-дни), есть функция age(), которая учитывает календарь, а не просто секунды:
SELECT age(now(), created_at)
FROM orders;


А когда требуется число дней или часов для аналитики, интервал можно сразу привести к нужной единице:
EXTRACT(EPOCH FROM now() - created_at) / 3600


🔥 Полезно для TTL-логики, SLA-метрик, мониторинга задержек и любых задач, где время жизни записи, является ключевым параметром.

➡️ SQL Ready | #совет

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

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

FlowchartAI — это бесплатный AI-генератор блок-схем, который из текста или кода автоматически строит диаграмму. Он анализирует твой ввод (описание процесса/алгоритма или код) и превращает его в визуальную блок-схему, которую можно смотреть прямо в браузере без регистрации.

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

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

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

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

💡 Отличную статью нашёл на Хабре: «Демобаза 2.0 для PostgreSQL»!

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


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


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

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

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

👍 TutorialsPoint — сайт с теорий и практикой в одном месте!

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

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

➡️ SQL Ready | #сайт

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

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

WHERE vs HAVING — фильтрация строк и фильтрация групп!

В SQL иногда путают WHERE и HAVING, потому что внешне они решают похожую задачу — отфильтровать результат. На практике это два разных этапа выполнения запроса с разной семантикой.

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

orders(id, customer_id, amount)


Нужно выбрать заказы дороже 100.

Корректный и очевидный вариант:
SELECT *
FROM orders
WHERE amount > 100;


WHERE применяется до агрегации и фильтрует отдельные строки исходной таблицы.

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

Интуитивная, но неверная попытка:
SELECT customer_id, SUM(amount) AS total_amount
FROM orders
WHERE SUM(amount) > 1000
GROUP BY customer_id;


Этот запрос некорректен: агрегатные функции не могут использоваться в WHERE, потому что на момент применения WHERE агрегатов ещё не существует.

Правильный вариант — HAVING:
SELECT customer_id, SUM(amount) AS total_amount
FROM orders
GROUP BY customer_id
HAVING SUM(amount) > 1000;


HAVING применяется после GROUP BY и фильтрует уже сформированные группы.

Важно понимать разницу по этапам: WHERE — фильтрует строки до группировки; GROUP BY — формирует группы; HAVING — фильтрует группы; SELECT — формирует итоговый результат.

Частая оптимизационная ошибка — переносить условия из WHERE в HAVING без необходимости:
SELECT customer_id, SUM(amount)
FROM orders
GROUP BY customer_id
HAVING amount > 100;


Такой запрос либо не выполнится, либо будет логически неверным: amount — это поле строки, а HAVING работает с группой.

Корректный и оптимальный вариант — комбинировать:
SELECT customer_id, SUM(amount) AS total_amount
FROM orders
WHERE amount > 100
GROUP BY customer_id
HAVING SUM(amount) > 1000;


Здесь: WHERE отсекает мелкие заказы до агрегации (меньше данных); HAVING проверяет условие на уровне группы.

🔥 Используй WHERE для фильтрации строк и HAVING — только для условий на агрегаты и группы. Это влияет не только на корректность, но и на производительность запроса

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

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