Разбор типовых приёмов классификации данных, обработки 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
Например,
Transport Subsystem — приём запроса от клиента по сетевому протоколу, установка сессии, а Query Processor — синтаксический и семантический разбор, построение дерева запроса (parse tree).На схеме показан полный путь запроса — от клиента до хранения данных.
Сохрани, чтобы не забыть!
Please open Telegram to view this post
VIEW IN TELEGRAM
❤15👍9🔥8🤝3
Оконные агрегаты, как считать по группе и не терять строки!
Оконные функции удобны не только для ранжирования. Частая задача на практике — посчитать агрегаты по группе и при этом оставить каждую строку исходной таблицы.
Представим таблицу заказов:
Хотим увидеть все заказы и одновременно понимать, сколько денег каждый клиент потратил в сумме.
Первая мысль — посчитать через
Да, сумму он посчитает. Но список заказов пропадёт — останется по одной строке на клиента.
Если коротко:
Решение через оконную функцию:
В результате каждая строка — отдельный заказ и
Фильтрация по агрегату без
Обратите внимание: фильтрация происходит уже после вычисления оконной функции. Никаких
Та же логика через
Работает, но выглядит тяжелее: отдельный подзапрос, джойн, дублирование таблицы.
Можно посчитать сразу несколько агрегатов по тем же окнам:
Получаем и общую сумму, и количество заказов клиента — прямо в каждой строке.
🔥 Используйте оконные функции, когда нужно посчитать итог по группе, но не потерять сами строки. Это базовый приём аналитического SQL, который постоянно встречается в реальных задачах.
➡️ SQL Ready | #практика
Оконные функции удобны не только для ранжирования. Частая задача на практике — посчитать агрегаты по группе и при этом оставить каждую строку исходной таблицы.
Представим таблицу заказов:
orders(id, customer_id, amount, created_at)
Хотим увидеть все заказы и одновременно понимать, сколько денег каждый клиент потратил в сумме.
Первая мысль — посчитать через
GROUP BY:SELECT
customer_id,
SUM(amount) AS total_amount
FROM orders
GROUP BY customer_id;
Да, сумму он посчитает. Но список заказов пропадёт — останется по одной строке на клиента.
Если коротко:
GROUP BY считает по группе, но детали внутри группы выбрасывает.Решение через оконную функцию:
SELECT
id,
customer_id,
amount,
SUM(amount) OVER (PARTITION BY customer_id) AS total_amount
FROM orders;
PARTITION BY customer_id разбивает строки на независимые окна по клиентам.SUM(...) OVER (...) считает сумму внутри каждого окна, но строки не схлопывает — каждая остаётся на месте.В результате каждая строка — отдельный заказ и
total_amount — сумма всех заказов этого клиента.Фильтрация по агрегату без
GROUP BY — например, нужно выбрать только заказы тех клиентов, у которых суммарный оборот больше 1000:SELECT *
FROM (
SELECT
*,
SUM(amount) OVER (PARTITION BY customer_id) AS total_amount
FROM orders
) t
WHERE total_amount > 1000;
Обратите внимание: фильтрация происходит уже после вычисления оконной функции. Никаких
GROUP BY, никаких дополнительных JOIN.Та же логика через
JOIN (для сравнения):SELECT o.*
FROM orders o
JOIN (
SELECT customer_id, SUM(amount) AS total_amount
FROM orders
GROUP BY customer_id
) s ON s.customer_id = o.customer_id
WHERE s.total_amount > 1000;
Работает, но выглядит тяжелее: отдельный подзапрос, джойн, дублирование таблицы.
Можно посчитать сразу несколько агрегатов по тем же окнам:
SELECT
id,
customer_id,
amount,
SUM(amount) OVER (PARTITION BY customer_id) AS total_amount,
COUNT(*) OVER (PARTITION BY customer_id) AS orders_count
FROM orders;
Получаем и общую сумму, и количество заказов клиента — прямо в каждой строке.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍18❤13🔥8
This media is not supported in your browser
VIEW IN TELEGRAM
Удобный онлайн-редактор, где можно быстро проверить SQL-запросы без установки СУБД и настройки окружения. Встроенные примеры позволяют сразу попробовать SELECT, JOIN, агрегатные функции, подзапросы и другие конструкции на готовых таблицах. Подходит для обучения, практики, подготовки к собесам. Также можно использовать, как мини шпаргалку.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤15👍9🔥7🤝2
Проверка наличия связанных данных!
Когда нужно проверить существование связанных строк, обычный
В отличие от
🔥
➡️ 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 делает то же самое логически, но чаще требует больше работы оптимизатора и может уступать по производительности на больших объёмах.Please open Telegram to view this post
VIEW IN TELEGRAM
❤17👍11🤝8
В этой статье:
• Показано, как устроена учебная база, приближенная к реальным продакшен-сценариям с миллионами записей;
• Разобрано, как генерируются правдоподобные данные через имитацию работы системы, включая бронирования и жизненный цикл событий;
• Объясняется, какие задачи по SQL, аналитике и производительности можно отрабатывать на такой базе.🔊 Продолжайте читать на Habr!
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11👍9🤝8❤1
TRUNC(date) — как правильно обрезать дату в Oracle без потери индекса!
В Oracle тип
Представим таблицу:
Нужно выбрать заказы за 18 февраля 2026.
Интуитивный вариант:
Логически верно, но есть проблема.
Функция применяется к колонке — поэтому обычный индекс по
Правильный способ — диапазон по дате:
Теперь условие индекс-дружелюбное (sargable) — оптимизатор может использовать обычный индекс по
Этот подход корректно работает независимо от времени в поле.
Если всё же нужно часто фильтровать по
После этого запрос с
Функция
Поддерживаются форматы: DD (день), MM (месяц), YYYY (год), IW (ISO-неделя) и др.
🔥 Для фильтрации по дате используйте диапазоны, а
➡️ SQL Ready | #практика
В 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 — для отображения, агрегации или вместе с функциональным индексом.Please open Telegram to view this post
VIEW IN TELEGRAM
🔥13👍9🤝8❤3
This media is not supported in your browser
VIEW IN TELEGRAM
FlowchartAI — это бесплатный AI-генератор блок-схем, который из текста или кода автоматически строит диаграмму. Он анализирует твой ввод (описание процесса/алгоритма или код) и превращает его в визуальную блок-схему, которую можно смотреть прямо в браузере без регистрации.
📌 Оставляю ссылочку: flowchartai.org
➡️ SQL Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
👍13❤8🤝8👎1
👍14🔥9🤝9❤1
This media is not supported in your browser
VIEW IN TELEGRAM
Сервис позволяет писать, запускать и проверять SQL-запросы прямо в браузере. Можно создать схему базы, заполнить тестовыми данными и сразу увидеть результат выполнения, идеально для экспериментов, обучения и разбора задач. Поддерживает разные СУБД (MySQL, PostgreSQL, Oracle, SQL Server и др.), поэтому удобно сравнивать поведение запросов.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥14👍11❤10
Вычисление временных интервалов между датами!
PostgreSQL умеет возвращать полноценный интервал одной операцией:
Если нужно получить возраст записи в читаемом виде (годы-месяцы-дни), есть функция
А когда требуется число дней или часов для аналитики, интервал можно сразу привести к нужной единице:
🔥 Полезно для TTL-логики, SLA-метрик, мониторинга задержек и любых задач, где время жизни записи, является ключевым параметром.
➡️ SQL Ready | #совет
PostgreSQL умеет возвращать полноценный интервал одной операцией:
now() - created_at
Если нужно получить возраст записи в читаемом виде (годы-месяцы-дни), есть функция
age(), которая учитывает календарь, а не просто секунды:SELECT age(now(), created_at)
FROM orders;
А когда требуется число дней или часов для аналитики, интервал можно сразу привести к нужной единице:
EXTRACT(EPOCH FROM now() - created_at) / 3600
Please open Telegram to view this post
VIEW IN TELEGRAM
❤15👍9🔥9
Например,
SELECT отвечает за выборку данных, FROM — за указание источника (таблицы), а WHERE позволяет отфильтровать строки по условию.На картинке — основные операторы SQL, типы JOIN’ов и правильный порядок выполнения запроса внутри СУБД.
Сохрани, чтобы не забыть!
Please open Telegram to view this post
VIEW IN TELEGRAM
👍19❤10🔥9