SQL Academy: всё о реляционных БД и SQL
По всем вопросам и коммерческим предложениям писать @LadanovNick Купить рекламу: https://telega.in/c/sqlacademyofficial Чат студентов SQL Academy https://t.me/sqlacademyorg
Больше📈 Аналитический обзор Telegram-канала SQL Academy: всё о реляционных БД и SQL
Канал SQL Academy: всё о реляционных БД и SQL (@sqlacademyofficial) языкового сегмента Русский является активным участником. Сейчас сообщество объединяет 11 386 подписчиков, занимая 10 884 место в категории Технологии и приложения и 57 210 место в регионе Россия.
📊 Показатели аудитории и динамика
С момента создания невідомо проект демонстрирует стремительный рост, собрав аудиторию из 11 386 подписчиков.
Согласно последним данным от 07 июля, 2026, канал показывает стабильную активность. За последние 30 дней изменение числа участников составило 136, а за последние 24 часа — 8, при этом общий охват остаётся высоким.
- Статус верификации: Не верифицирован
- Уровень вовлечённости (ER): Средний показатель вовлечённости аудитории составляет 15.32%. В первые 24 часа после публикации контент обычно набирает 10.66% реакций от общего числа подписчиков.
- Охват публикаций: В среднем каждый пост получает 1 744 просмотров. В течение первых суток публикация набирает 1 213 просмотров.
- Реакции и взаимодействия: Аудитория активно поддерживает контент: среднее количество реакций на один пост — 29.
- Тематические интересы: Контент сосредоточен на ключевых темах, таких как sql, строка, индекс, auto_increment, created_at.
📝 Описание и контентная политика
Автор описывает ресурс как площадку для выражения субъективного мнения:
“По всем вопросам и коммерческим предложениям писать @LadanovNick
Купить рекламу: https://telega.in/c/sqlacademyofficial
Чат студентов SQL Academy
https://t.me/sqlacademyorg”
Благодаря высокой частоте обновлений (последние данные получены 08 июля, 2026) канал поддерживает актуальность и высокий уровень охвата публикаций. Аналитика показывает, что аудитория активно взаимодействует с контентом, что делает его важной точкой влияния в категории Технологии и приложения.
DATE_ADD(date, INTERVAL N unit)
DATE_SUB(date, INTERVAL N unit)
unit бывает: SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, YEAR и их комбинации (например, DAY_MICROSECOND).
2️⃣ Простые примеры
-- дата через 7 дней
SELECT DATE_ADD('2025-05-01', INTERVAL 7 DAY); -- 2025-05-08
-- месяц назад
SELECT DATE_SUB(NOW(), INTERVAL 1 MONTH);
-- +2 часа к текущему времени
SELECT DATE_ADD(NOW(), INTERVAL 2 HOUR);
3️⃣ Кейс: отчёт «за последние 30 дней»
SELECT *
FROM sales
WHERE sale_date >= DATE_SUB(CURDATE(), INTERVAL 30 DAY);
👉 CURDATE() = 00:00:00 текущего дня, поэтому попадут 30 «полных» дней.
4️⃣ Прибавляем месяцы правильно
DATE_ADD учитывает разную длину месяцев и високосные годы:
-- 31 января + 1 месяц → 28 февраля (или 29 в високосный год)
SELECT DATE_ADD('2024-01-31', INTERVAL 1 MONTH); -- 2024-02-29
⚠️ Подводные камни
1. Точность NOW(): если нужен только день без времени — используйте CURDATE().
2. Интервалы с недельной гранулярностью: INTERVAL 1 WEEK = 7 дней, не календарная неделя.
-- с 3-й позиции длиной 4 символа
SELECT SUBSTRING('ABCDEF', 3, 4);
-- результат: 'CDEF'
-- начиная с 2-го символа до конца
SELECT SUBSTRING('123456', 2);
-- результат: '23456'
-- отрицательный индекс = отсчёт от конца
SELECT SUBSTRING('hello', -2);
-- результат: 'lo’
2️⃣ LEFT(str, N) — «первые N символов»
SELECT LEFT('8901234567', 2);
-- '89' (код страны)
3️⃣ RIGHT(str, N) — «последние N символов»
SELECT RIGHT('2025-04-30', 2);
-- '30' (день)
💡 Практический пример: разбираем телефон
SELECT
phone,
LEFT(phone, 2) AS country_code,
-- первые 2 цифры
SUBSTRING(phone, 3, 3) AS city_code,
-- далее 3 цифры
RIGHT(phone, 4) AS last_digits
-- последние 4
FROM contacts;
⚠️ Подводные камни
🔹 Нумерация с 1: первый символ — позиция 1, а не 0.
🔹 NULL → NULL: любое из функций вернёт NULL, если аргумент NULL — учитывайте в отчётах.
✅ Итог
🔹SUBSTRING — универсальный нож: позиция + длина.
🔹LEFT / RIGHT — быстрый способ взять начало или конец строки.
-- создаём последовательность с шага 1, стартуем с 10 000
CREATE SEQUENCE ticket_seq
START WITH 10000
INCREMENT BY 1;
2️⃣ Вставка чисел в таблицу
INSERT INTO tickets (id, user_id, created_at)
VALUES (NEXT VALUE FOR ticket_seq, 42, NOW());
Получаем уникальный id без AUTO_INCREMENT в самой таблице.
3️⃣ Один счётчик — много таблиц
Нужно, чтобы tickets и invoices шли в общей нумерации? Просто используйте ту же последовательность:
INSERT INTO invoices (id, total)
VALUES (NEXT VALUE FOR ticket_seq, 199.00);
4️⃣ Дополнительные возможности
-- увеличить шаг
ALTER SEQUENCE ticket_seq
INCREMENT BY 10;
-- обнулить (осторожно!)
ALTER SEQUENCE ticket_seq
RESTART WITH 50000;
-- посмотреть текущее значение
SELECT LAST_VALUE
FROM INFORMATION_SCHEMA.SEQUENCES
WHERE SEQUENCE_NAME = 'ticket_seq';
5️⃣ Сравнение с AUTO_INCREMENT 🤔
🔹 Объект:
– SEQUENCE — отдельный объект в схеме
– AUTO_INCREMENT — жёстко привязан к таблице
🔹Один счётчик для нескольких таблиц:
– SEQUENCE — да, можно использовать в любой таблице
– AUTO_INCREMENT — нет, только внутри своей таблицы
🔹Задать старт / шаг:
– SEQUENCE — гибко: START WITH, INCREMENT BY для каждой последовательности
– AUTO_INCREMENT — глобальные AI_OFFSET / AI_INCREMENT, меньше контроля
🔹Сброс счётчика:
– SEQUENCE — ALTER SEQUENCE … RESTART WITH n
– AUTO_INCREMENT — ALTER TABLE … AUTO_INCREMENT = n (только вверх от MAX(id))
✅ Итог
SEQUENCE = гибкий, «шарящийся» счётчик, который:
🔹Позволяет одной командой нумеровать разные таблицы.
🔹Даёт точный контроль над шагом и стартом.
NTILE(N) OVER (PARTITION BY … ORDER BY …)
🔹 N — число групп (4 = квартели).
🔹 PARTITION BY (необязательно) — создаёт независимые группы (например, по стране).
🔹 ORDER BY — признак, по которому сортируем и считаем ранг.
2️⃣ Классический пример: 4 квартиля по количеству заказов
SELECT
user_id,
orders_count,
NTILE(4) OVER
(ORDER BY orders_count DESC) AS quartile
FROM user_stats
ORDER BY orders_count DESC;
🔹 Пользователи с наибольшим orders_count попадают в quartile = 1, а с наименьшим — в quartile = 4.
🔹 Если строк не делится поровну, верхние группы получат на 1 запись больше (MySQL делит «сверху»).
3️⃣ Квартели внутри каждой страны
SELECT
country,
user_id,
orders_count,
NTILE(4) OVER (
PARTITION BY country
ORDER BY orders_count DESC
) AS quartile
FROM user_stats;
Теперь каждый блок «страна» делится на свои 4 части — удобно для локальных лидеров.
4️⃣ Как использовать результат
🔹Маркетинг: таргетировать акции только quartile = 4 (самые неактивные).
🔹Аналитика: сравнить средний чек между квартилями.
🔍 Потренируйтесь
Откройте готовый плейграунд, запустите NTILE(4) и поэкспериментируйте с разным числом групп.
👉 https://sqlplayground.app/sandbox/683ef210cb9fcbe8d3db3a9e
✅ Итог
🔹NTILE(N) быстро делит данные на равные сегменты.
🔹Работает вместе с PARTITION BY и любой сортировкой.
🔹Незаменим при сегментации пользователей, товаров или чеков.
-- страница 3, по 20 записей
SELECT *
FROM posts
ORDER BY created_at DESC
LIMIT 20 OFFSET 40;
Работает просто, но есть три проблемы:
🔹 OFFSET N сначала пропускает N строк → чем дальше, тем медленнее.
🔹 Если за время прокрутки добавятся новые посты, пользователь увидит дубликаты или пропуски.
🔹При больших значениях OFFSET сервер читает тысячи строк зря.
2️⃣ Key-set pagination (a.k.a. «лента без дыр»)
Идея: вместо «пропустить N» передаём последний id/дату уже загруженной записи и берём следующие сверху.
-- первый запрос (стартовая лента)
SELECT *
FROM posts
ORDER BY created_at DESC
LIMIT 20;
-- фронт получает последнюю дату
→ next_cursor = '2025-05-26 10:15:00'
-- второй запрос (следующая порция)
SELECT *
FROM posts
WHERE created_at < '2025-05-26 10:15:00'
ORDER BY created_at DESC
LIMIT 20;
Плюсы:
✅ Нет медленного OFFSET — используется индекс по created_at.
✅ Новые посты не «вставляются» в уже просмотренную часть, дубликатов нет.
✅ Маркер-курсор можно прятать в URL или JSON‐ответе.
3️⃣ Что передавать в качестве курсора?
🔹 Авто-инкремент id — легко, но если бывают «дыры» (удаления) — используйте < id.
🔹Дата/время — удобно, но в редких случаях возможны совпадения секунд. Можно добавить второй критерий:
WHERE (created_at < :cursor_date)
OR (created_at = :cursor_date AND id < :cursor_id)
4️⃣ Советы и грабли ⚠️
🔹Создайте индекс (created_at DESC) или (created_at, id) — иначе даже key-set будет сканировать всю таблицу.
🔹Никогда не показывайте номер страницы пользователю, если используете key-set: курсор — это дата/ID, а не «страница 5».
🔹Для поиска по разным фильтрам (например, user_id) добавляйте их в индекс (user_id, created_at).
✅ Итог
🔹LIMIT + OFFSET подходит для админ-таблиц или маленьких данных.
🔹Для «бесконечных» лент используйте key-set pagination: быстрее, без дубликатов, идеально для мобильных фидов в духе Instagram/ТикТок.
Потренируйтесь: создайте таблицу posts, сгенерируйте 100 000 строк, сравните скорость OFFSET 90000 и key-set. Результат удивит! 🚀
SELECT name, is_vip
FROM customers
ORDER BY
CASE WHEN is_vip = 1 THEN 0 ELSE 1 END,
name;
-- внутри групп сортируем по алфавиту
▶️ CASE возвращает 0 для VIP, 1 — для всех остальных.
▶️ В порядке сортировки 0 идёт раньше 1, поэтому VIP‑ы будут первыми
🔹Сложнее: приоритеты A > B > C → остальные
SELECT name, status
FROM orders
ORDER BY CASE status
WHEN 'A' THEN 0
WHEN 'B' THEN 1
WHEN 'C' THEN 2
ELSE 3
END,
created_at DESC;
▶️ Сначала все со статусом A, потом B, затем C, затем остальные.
▶️ Внутри каждой группы — свежие заказы выше (по дате).
Типичные ошибки ⚠️
1️⃣ NULL‑ы: если столбец может быть NULL, добавьте явную проверку
CASE WHEN col IS NULL THEN …
2️⃣ Числа вместо строк: не забывайте, что CASE должен возвращать однотипные значения (в примерах — всегда INT).
3️⃣ Производительность: выражение в ORDER BY отключает использование индекса.
Для больших таблиц можно создать сгенерированный столбец с тем же CASE и проиндексировать его.
ALTER TABLE customers
ADD vip_sort TINYINT AS (CASE WHEN is_vip = 1 THEN 0 ELSE 1 END) STORED,
ADD INDEX (vip_sort);
Итоги ✅
🔸CASE в ORDER BY даёт гибкую условную сортировку без подзапросов.
🔸Легко расставить приоритеты: VIP, скидки, статусы…
🔸Следите за NULL‑ами и индексацией при больших объёмах.