WHERE vs HAVING — фильтрация строк и фильтрация групп!
В SQL иногда путают
Представим таблицу заказов:
Нужно выбрать заказы дороже 100.
Корректный и очевидный вариант:
Теперь другая задача: найти клиентов, у которых сумма всех заказов больше 1000.
Интуитивная, но неверная попытка:
Этот запрос некорректен: агрегатные функции не могут использоваться в
Правильный вариант —
Важно понимать разницу по этапам:
Частая оптимизационная ошибка — переносить условия из
Такой запрос либо не выполнится, либо будет логически неверным:
Корректный и оптимальный вариант — комбинировать:
Здесь:
🔥 Используй
➡️ SQL Ready | #практика
В 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 — только для условий на агрегаты и группы. Это влияет не только на корректность, но и на производительность запросаPlease open Telegram to view this post
VIEW IN TELEGRAM
🔥20👍14❤12🤝1
LATERAL — коррелированные подзапросы!
Часто бывает нужно для каждой строки основной таблицы взять одну лучшую связанную строку - последнюю, первую, максимальную, минимальную, валидную.
Обычно это делают через оконки, CTE или GROUP BY + JOIN, что сложно читать и часто ломается при расширении логики:
Выполняет подзапрос для каждой строки слева, подставляя её значения.
Гарантирует, что ты берёшь именно нужную запись:
Потому что связь уже внутри подзапроса, JOIN тут формальность.
🔥 Это отличный инструмент, когда нужна одна зависимая строка на родителя
➡️ SQL Ready | #совет
Часто бывает нужно для каждой строки основной таблицы взять одну лучшую связанную строку - последнюю, первую, максимальную, минимальную, валидную.
Обычно это делают через оконки, CTE или GROUP BY + JOIN, что сложно читать и часто ломается при расширении логики:
LEFT JOIN LATERAL (...)
Выполняет подзапрос для каждой строки слева, подставляя её значения.
ORDER BY created_at DESC
LIMIT 1
Гарантирует, что ты берёшь именно нужную запись:
ON true
Потому что связь уже внутри подзапроса, JOIN тут формальность.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍15🔥8🤝8
This media is not supported in your browser
VIEW IN TELEGRAM
Если хочешь не просто учить синтаксис, а писать реальные запросы и практиковаться. Это отличный способ закрепить знания по выборкам, фильтрации, сортировке, условиям и множеству других тем, особенно если готовишься к собесам.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤14🔥9👍8
Частая проблема в аналитике и BI - таблицы с десятками колонок?
Пока колонок три это может быть терпимо. Когда их становится двадцать и больше, запросы, джоины и графики начинают разваливаться.
На выходе получается универсальная структура
Полезно, когда нужно сравнивать метрики между собой
или передавать данные в BI без кастомной логики:
🔥 Один запрос - десятки метрик и минимум изменений при росте отчёта (новые метрики просто добавляются в список UNPIVOT).
➡️ SQL Ready | #совет
Пока колонок три это может быть терпимо. Когда их становится двадцать и больше, запросы, джоины и графики начинают разваливаться.
UNPIVOT позволяет превратить колонки в строки на уровне SQL, без переписывания данных и логики приложения (синтаксис зависит от СУБД):UNPIVOT (value FOR metric IN (views, clicks))
На выходе получается универсальная структура
metric / value, которая хорошо ложится в агрегации, фильтры и визуализации BI.Полезно, когда нужно сравнивать метрики между собой
или передавать данные в BI без кастомной логики:
WHERE metric IN ('views', 'orders')Please open Telegram to view this post
VIEW IN TELEGRAM
👍15🔥8❤6🤝1
Например, шардинг позволяет масштабировать базу данных, распределяя данные по нескольким серверам, а правильно выбранный ключ шарда напрямую влияет на производительность и равномерность нагрузки.
На картинке — основные принципы шардинга баз данных, которые стоит держать под рукой.
Сохрани, чтобы не забыть!
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥16❤10🤝8
ORDER BY в подзапросах: почему порядок не гарантируется!
Причина в том, что оптимизатор может свободно менять план выполнения, пока итоговый результат формально остаётся корректным.
Таблица событий:
Попытка взять последние события через подзапрос:
Несмотря на явный
Это означает, что порядок строк на выходе не определён, и СУБД имеет право проигнорировать внутреннюю сортировку как не влияющую на результат.
Корректный вариант — сортировать во внешнем запросе:
Именно этот
Когда
В этом случае сортировка в подзапросе используется для выбора нужных строк, а не для их отображения.
Внутренний
Аналогичный принцип работает с оконными функциями:
Здесь
🔥
➡️ SQL Ready | #практика
ORDER BY управляет порядком строк только в финальном результате запроса. В подзапросах и CTE он не гарантирует порядок строк, если не используется вместе с ограничением количества возвращаемых данных.Причина в том, что оптимизатор может свободно менять план выполнения, пока итоговый результат формально остаётся корректным.
Таблица событий:
events(id, user_id, created_at)
Попытка взять последние события через подзапрос:
SELECT *
FROM (
SELECT *
FROM events
ORDER BY created_at DESC
) t;
Несмотря на явный
ORDER BY внутри подзапроса, внешний запрос не содержит сортировки.Это означает, что порядок строк на выходе не определён, и СУБД имеет право проигнорировать внутреннюю сортировку как не влияющую на результат.
Корректный вариант — сортировать во внешнем запросе:
SELECT *
FROM events
ORDER BY created_at DESC;
Именно этот
ORDER BY гарантирует порядок строк, который получит клиент или приложение.Когда
ORDER BY в подзапросе имеет смысл — он становится значимым только вместе с ограничением:SELECT *
FROM (
SELECT *
FROM events
ORDER BY created_at DESC
LIMIT 10
) t
ORDER BY created_at DESC;
В этом случае сортировка в подзапросе используется для выбора нужных строк, а не для их отображения.
Внутренний
ORDER BY определяет, какие именно 10 строк попадут в подзапрос, а внешний ORDER BY задаёт порядок финального результата.Аналогичный принцип работает с оконными функциями:
SELECT id, user_id, created_at
FROM (
SELECT
id,
user_id,
created_at,
ROW_NUMBER() OVER (
PARTITION BY user_id
ORDER BY created_at DESC
) AS rn
FROM events
) s
WHERE rn = 1;
Здесь
ORDER BY влияет на вычисление ROW_NUMBER, то есть на логику нумерации строк внутри окна, но не определяет порядок вывода строк в результирующем наборе.ORDER BY — это про порядок выдачи результата, а не про порядок хранения данных. Без внешнего ORDER BY СУБД не обязана возвращать строки в каком-либо определённом порядке.Please open Telegram to view this post
VIEW IN TELEGRAM
🔥16❤13👍12
В этой статье:
• Разбирается, как вынести тяжёлые отчёты 1С с мастер-базы на реплику;
• Показывается настройка репликации PostgreSQL/Tantor DB под реальные сценарии 1С;
• Анализируются узкие места, задержки репликации и влияние отчётных запросов;
• Даются результаты нагрузочного тестирования и выводы, которые можно применить в системах.🔊 Продолжайте читать на Habr!
Please open Telegram to view this post
VIEW IN TELEGRAM
👍13❤9🔥8😁1
👍14🔥11🤝9👎1
Одна таблица играет сразу несколько ролей?
Иногда данные уже есть в одной таблице, но логически это разные сущности.
Классический пример, иерархия: сотрудник и его менеджер хранятся
в одной таблице
Физически это одна и та же таблица, но логически, разные роли:
Связь читается так, у сотрудника есть
🔥 Такой приём используется не только для менеджеров. Категории и родительские категории, комментарии и ответы, версии сущностей, связи предыдущий / следующий, графы, цепочки состояний и др.
➡️ SQL Ready | #совет
Иногда данные уже есть в одной таблице, но логически это разные сущности.
Классический пример, иерархия: сотрудник и его менеджер хранятся
в одной таблице
employees:FROM employees e
JOIN employees m
e — это сотрудник, m — это менеджер.Физически это одна и та же таблица, но логически, разные роли:
ON m.id = e.manager_id
Связь читается так, у сотрудника есть
manager_id, который указывает на id другого сотрудника.Please open Telegram to view this post
VIEW IN TELEGRAM
❤13👍13🤝8
Разбор типовых приёмов классификации данных, обработки NULL, подстановки значений по условиям и вычисления итоговых результатов прямо в запросах. Полезно для отчётов, аналитики, бизнес-логики в SELECT, упрощения WHERE и ORDER BY, а также для написания компактного и читаемого SQL без изменения схемы БД.Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
❤14🤝14👍12
This media is not supported in your browser
VIEW IN TELEGRAM
Этот репозиторий построен как полноценный тренажёр: реальные схемы данных, последовательные задания и разборы запросов разной сложности. Помогает системно разобраться в работе с базами данных: выборки, сложные JOIN, агрегации, подзапросы, фильтрация, изменение данных и др.
Оставляю ссылочку: GitHub📱
Please open Telegram to view this post
VIEW IN TELEGRAM
👍15🤝9❤7
NTILE — разбиение данных на равные группы внутри партиций!
Оконные функции полезны не только для топов и агрегатов. Частый аналитический кейс — разложить данные по корзинам одинакового размера: квартилям, децилям, сегментам.
Представим таблицу:
Нужно разбить пользователей каждой страны на 4 группы по выручке — от меньшей к большей.
Решение через
Результат:
Важно: группы не всегда будут строго одинаковыми по размеру. Если строк не делится нацело, первые группы получат на одну строку больше.
Если убрать
Разбиение произойдёт по всей таблице, без учёта страны.
Практический кейс — сегментация:
Так можно выбрать топ-10% пользователей в каждой стране.
🔥 Используйте
➡️ SQL Ready | #практика
Оконные функции полезны не только для топов и агрегатов. Частый аналитический кейс — разложить данные по корзинам одинакового размера: квартилям, децилям, сегментам.
Представим таблицу:
users(id, country, revenue)
Нужно разбить пользователей каждой страны на 4 группы по выручке — от меньшей к большей.
Решение через
NTILE:SELECT
id,
country,
revenue,
NTILE(4) OVER (
PARTITION BY country
ORDER BY revenue
) AS revenue_quartile
FROM users;
NTILE(4) распределяет строки внутри каждой партиции на 4 группы максимально равного размера.ORDER BY определяет, какие значения попадут в «нижние» и «верхние» группы.Результат:
quartile = 1 — пользователи с минимальной выручкой, quartile = 4 — пользователи с максимальной выручкойВажно: группы не всегда будут строго одинаковыми по размеру. Если строк не делится нацело, первые группы получат на одну строку больше.
Если убрать
PARTITION BY:NTILE(4) OVER (ORDER BY revenue)
Разбиение произойдёт по всей таблице, без учёта страны.
Практический кейс — сегментация:
SELECT *
FROM (
SELECT
*,
NTILE(10) OVER (
PARTITION BY country
ORDER BY revenue DESC
) AS decile
FROM users
) t
WHERE decile = 1;
Так можно выбрать топ-10% пользователей в каждой стране.
NTILE работает построчно и не агрегирует данные — каждая строка сохраняется, а группа добавляется как аналитическая метка.NTILE, когда нужно сегментировать данные внутри группы на равные части без предварительной агрегации.Please open Telegram to view this post
VIEW IN TELEGRAM
👍14❤11🔥8🤝2
This media is not supported in your browser
VIEW IN TELEGRAM
Серия коротких и чётких микро-курсов с упором на практику: каждый модуль - это конкретная тема, упражнения и работа с настоящими датасетами прямо в браузере. Подходит для быстрого освоения основ языка, закрепления синтаксиса и понимания, как Python используется в реальных задачах.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥15👍9🤝8
Постоянные представления, как слой архитектуры!
Логика фильтрации находится в одном месте, без копирования условий по коду.
🔥 Права можно выдавать на
➡️ SQL Ready | #совет
VIEW — сохранённый запрос, как объект схемы. Для обычных VIEW данные не дублируются: оптимизатор, как правило, раскрывает их в подзапрос и строит единый план (зависит от СУБД):SELECT *
FROM active_users
WHERE email LIKE '%@company.com';
Логика фильтрации находится в одном месте, без копирования условий по коду.
CREATE OR REPLACE VIEW active_users AS
SELECT id, name, email
FROM users
WHERE deleted_at IS NULL
AND NOT is_blocked;
CREATE OR REPLACE позволяет централизованно менять логику (если сохраняется совместимый контракт колонок), не переписывая клиентские запросы:GRANT SELECT ON active_users TO reporting_role;
VIEW, а не на таблицу, скрывая лишние поля и фиксируя допустимую проекцию (механика зависит от СУБД).Please open Telegram to view this post
VIEW IN TELEGRAM
❤13🔥13👍9🤝2
This media is not supported in your browser
VIEW IN TELEGRAM
Это полноценный бесплатный ресурс в формате репозитория, который даёт основу работы с реляционными базами данных. Здесь структура БД, таблицы, SELECT, JOIN, подзапросы, агрегаты, изменение данных и экспорт баз. Материал ориентирован на реальные задачи DevOps, backend-разработки и системного администрирования.
Оставляю ссылочку: GitHub📱
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥15👍8❤7🤝4
Индекс для поиска по шаблону, который действительно работает!
Обычные B-tree индексы хорошо работают только для шаблонов вида
Расширение
После этого запросы вроде:
Начинают использовать индекс и ускоряются, особенно для поиска по почте, логинам, URL и другим текстовым идентификаторам:
🔥 Важно: триграммный индекс занимает больше места и замедляет INSERT/UPDATE, но для больших таблиц с поиском по подстроке это почти всегда оправдано.
➡️ SQL Ready | #совет
Обычные B-tree индексы хорошо работают только для шаблонов вида
prefix%. Но при поиске по подстроке (%text%) или сложных шаблонах они обычно не используются, и запрос превращается в полный скан таблицы даже на миллионах строк.CREATE EXTENSION IF NOT EXISTS pg_trgm;
Расширение
pg_trgm добавляет триграммные индексы, которые эффективно ищут подстроки, частичные совпадения и нечёткие шаблоны:CREATE INDEX users_email_trgm_idx
ON users USING gin (email gin_trgm_ops);
После этого запросы вроде:
SELECT *
FROM users
WHERE email ILIKE '%company%';
Начинают использовать индекс и ускоряются, особенно для поиска по почте, логинам, URL и другим текстовым идентификаторам:
EXPLAIN ANALYZE
SELECT *
FROM users
WHERE email ILIKE '%company%';
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥14❤11👍9