SQL Academy: всё о реляционных БД и SQL
По всем вопросам и коммерческим предложениям писать @LadanovNick Купить рекламу: https://telega.in/c/sqlacademyofficial Чат студентов SQL Academy https://t.me/sqlacademyorg
نمایش بیشتر📈 تحلیل کانال تلگرام SQL Academy: всё о реляционных БД и SQL
کانال SQL Academy: всё о реляционных БД и SQL (@sqlacademyofficial) در بخش زبانی روسی بازیگری فعال است. در حال حاضر جامعه شامل 11 356 مشترک است و جایگاه 10 920 را در دسته فناوری و برنامهها و رتبه 57 450 را در منطقه روسيا دارد.
📊 شاخصهای مخاطب و پویایی
از زمان ایجاد در невідомо، پروژه رشد سریعی داشته و 11 356 مشترک جذب کرده است.
بر اساس آخرین دادهها در تاریخ 26 ژوئن, 2026، کانال فعالیت پایداری دارد. در ۳۰ روز گذشته تغییر اعضا برابر 170 و در ۲۴ ساعت گذشته برابر 6 بوده و همچنان دسترسی گستردهای حفظ شده است.
- وضعیت تأیید: تأیید نشده
- نرخ تعامل (ER): میانگین تعامل مخاطب 15.08% است و در ۲۴ ساعت نخست پس از انتشار، محتوا معمولاً 11.53% واکنش نسبت به کل مشترکان کسب میکند.
- دسترسی پستها: هر پست به طور میانگین 1 712 بازدید دریافت میکند. در اولین روز معمولاً 1 309 بازدید جمعآوری میشود.
- واکنشها و تعامل: مخاطبان بهطور فعال حمایت میکنند؛ میانگین واکنش به هر پست 16 است.
- علایق موضوعی: محتوا بر موضوعات کلیدی مانند sql, строка, индекс, auto_increment, created_at تمرکز دارد.
📝 توضیح و سیاست محتوایی
نویسنده این فضا را محل بیان دیدگاههای شخصی توصیف میکند:
“По всем вопросам и коммерческим предложениям писать @LadanovNick
Купить рекламу: https://telega.in/c/sqlacademyofficial
Чат студентов SQL Academy
https://t.me/sqlacademyorg”
به لطف بهروزرسانیهای پرتکرار (آخرین داده در تاریخ 27 ژوئن, 2026)، کانال همواره بهروز و دارای دسترسی بالاست. تحلیلها نشان میدهد مخاطبان بهطور فعال با محتوا تعامل دارند و آن را به نقطه اثرگذاری مهم در دسته فناوری و برنامهها تبدیل کردهاند.
OFFSET сильно замедляет базу. Она читает тысячи тяжелых строк целиком только для того, чтобы их отбросить.
📉 В чём проблема
🔹 Представь поиск 100-й страницы в толстой энциклопедии. Вместо оглавления ты читаешь весь текст с первой страницы, пока не дойдёшь до сотой.
🔹 Так же делает база при OFFSET 100000: она собирает с диска все данные (длинные тексты, даты), отсчитывает ненужные сто тысяч строк и просто выбрасывает их. Это огромная трата ресурсов.
🚀 Решение — Поздний JOIN (Deferred Join)
Сначала мы быстро находим нужные идентификаторы, а уже к ним присоединяем полные данные:
SELECT p.*
FROM (
SELECT id FROM products
ORDER BY price
LIMIT 50 OFFSET 100000
) AS sub
JOIN products p ON p.id = sub.id;
⚙️ Как это работает
🔸 Внутренний запрос работает как оглавление. Он бежит только по легкому индексу (цене и id), мгновенно пропуская 100 тысяч записей, и выдает 50 нужных id.
🔸 Внешний JOIN обращается к самой таблице и загружает тяжелые данные только для этих 50 финальных строк.
💡 Этот контринтуитивный трюк спасет твою пагинацию, когда таблица огромная, а отказаться от навигации по номерам страниц нельзя.
CREATE TABLE users (
id INT PRIMARY KEY,
age INT,
CHECK (age >= 0 AND age <= 120)
);
Теперь нельзя вставить пользователя с возрастом -5 или 999 — MySQL просто не даст это сделать.
Синтаксис CHECK
🔸 Можно добавлять при создании таблицы (CREATE TABLE) или позже (ALTER TABLE).
🔸 Можно использовать AND, OR, арифметику, сравнения, функции (с ограничениями).
🔸 Поддерживается в MySQL 8.0+. В старых версиях CHECK игнорировался (!).
Пример с условием на строку:
CREATE TABLE products (
id INT PRIMARY KEY,
name VARCHAR(100),
category ENUM('book', 'game', 'toy'),
price DECIMAL(10,2),
CHECK (price >= 0)
);
Здесь мы запрещаем отрицательные цены.
Добавление ограничения в существующую таблицу:
ALTER TABLE users
ADD CONSTRAINT chk_age CHECK (age >= 0 AND age <= 120);
Что произойдёт при нарушении?
INSERT INTO users (id, age) VALUES (1, -10);
-- ERROR 3819 (HY000): Check constraint 'chk_age' is violated.
⚠️ Важно помнить:
🔹CHECK срабатывает только для новых данных — старые строки не проверяются.
🔹Если значение NULL, правило обычно не нарушается (NULL считается «неизвестным» и не сравнивается напрямую).
🔹Ошибку можно перехватывать в приложении, чтобы сообщить пользователю.
✅ Когда стоит использовать CHECK:
🔹 Для ограничений: возраст, положительная цена, длина строки, формат (через LIKE или REGEXP).
🔹Когда хотите, чтобы в таблице «по умолчанию» всегда были только корректные данные.
🔹Чтобы упростить валидацию и сделать БД самодостаточной (например, при работе с внешними источниками данных).А при мыслях об автоматизации задач и усилении своих скиллов, осекаешься - повышение квалифкации стоит десятки тысяч.
Больше 60% работодателей уже заложили бюджет на развитие команды в этом году. Просто большинство аналитиков никогда не просят. Потому что не знают, как.
Мы собрали полный гайд:
→ 8 шагов от «хочу прокачаться» до «компания всё оплатила»
→ Готовые обоснования для руководителя
→ Скрипты ответов на возражения
→ Чек-лист перед разговором с HR
Здесь - твоя грамотная заявка на корпоративный грант.
📄 Забирай методичку бесплатно → https://tglink.io/7516d0cb076c3f
Реклама. ООО "АЙТИ РЕЗЮМЕ". ИНН 4025460134. erid: 2W5zFHRhK4Did name dept salary 1 Анна IT 80 2 Борис IT 120 3 Вера IT 150 4 Глеб HR 60 5 Дина HR 90Ожидаемый результат:
dept median IT 120 ← центр из 3 HR 75 ← (60+90)/2Разбор 🧠 Что такое медиана на пальцах 🔹 Сортируем зарплаты по возрастанию 🔹 Если строк нечётно — берём центральную 🔹 Если чётно — среднее двух центральных Для отдела с зарплатами [40, 50, 70, 100] медиана = (50 + 70) / 2 = 60. 1️⃣ Что нам нужно знать про каждую строку Чтобы найти «центральную» строку в отделе, нужны две вещи: 🔹 её позиция в отсортированном списке внутри отдела 🔹 общее число строк в отделе Обе задачи решают оконные функции — ROW_NUMBER() и COUNT(*) с PARTITION BY department. 2️⃣ Как найти центральные позиции Формула для центра: 🔹 FLOOR((cnt + 1) / 2) — нижняя центральная 🔹 CEIL((cnt + 1) / 2) — верхняя центральная Магия в том, что для нечётного cnt обе формулы дают одну и ту же строку — это и есть единственная центральная. Для чётного — две соседние, которые мы потом усредним. Проверим на наших данных: 🔹 IT (cnt=3): FLOOR(4/2)=2, CEIL(4/2)=2 → берём строку №2 → зарплата 120 ✅ 🔹 HR (cnt=2): FLOOR(3/2)=1, CEIL(3/2)=2 → строки №1 и №2 → (60+90)/2 = 75 ✅ 3️⃣ Решение через оконные функции
WITH ranked AS (
SELECT
department,
salary,
ROW_NUMBER() OVER (
PARTITION BY department
ORDER BY salary
) AS rn,
COUNT(*) OVER (PARTITION BY department) AS cnt
FROM employees
)
SELECT
department,
AVG(salary * 1.0) AS median_salary
FROM ranked
WHERE rn IN (FLOOR((cnt + 1) / 2.0), CEIL((cnt + 1) / 2.0))
GROUP BY department;
CTE ranked внутри каждого отдела нумерует сотрудников от самой низкой зарплаты к самой высокой и параллельно считает общее число сотрудников. Внешний запрос оставляет только «центральные» строки и усредняет их.
На что обратить внимание❗️
🔹 Деление на 2.0, а не на 2 — иначе в некоторых БД получим целочисленное деление и логика для чётных отделов поедет
🔹 AVG(salary * 1.0) — та же история, если зарплаты хранятся в INT, без умножения на 1.0 результат округлится до целого
🔹 COUNT(*) посчитает и NULL-зарплаты как строки. Если такое возможно — используйте COUNT(salary) и заранее отфильтруйте WHERE salary IS NOT NULL, иначе ROW_NUMBER поставит NULL в начало и сломает порядок
🎁 Бонус: если PERCENTILE_CONT всё-таки доступен
В PostgreSQL, Oracle, SQL Server задача решается в одну строку:
SELECT
department,
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY salary) AS median
FROM employees
GROUP BY department;
Но на собесе обычно просят именно «руками» — чтобы проверить, понимаете ли вы, что происходит под капотом 🔍
🎯 Что запомнить
🔹 Медиана руками = ROW_NUMBER + COUNT в одном CTE
🔹 Центральные позиции — FLOOR и CEIL от (cnt+1)/2
🔹 Делим на 2.0, а не на 2 — иначе целочисленное деление всё сломает
🔹 Если в БД есть PERCENTILE_CONT — используйте его; ручная реализация нужна только для собесов и старых MySQL (до 8.0 там вообще нет оконных функций — придётся через переменные)Как это работает — коротко:
1. Таблица-клон
Rooms → триггер → Rooms_History
2. SYSTEM_TIME
Rooms → база сама ведёт историю
3. JSONB-слепки
Rooms → триггер → JSON-снимок в отдельную таблицу
1️⃣ Вариант 1: Таблица-клон
Самый понятный способ. Рядом с основной таблицей (например, Rooms) создаём её копию — Rooms_History. Каждый раз, когда кто-то меняет строку в основной таблице, автоматический механизм (триггер) сначала сохраняет старую версию строки в копию, и только потом применяет изменение.
Главная боль — Schema Drift. Допустим, разработчик добавил в основную таблицу новую колонку «площадь». Если он забыл добавить эту же колонку в таблицу-клон и обновить триггер — всё ломается. Подходит только там, где структура таблиц не менялась годами и не планирует.
2️⃣ Вариант 2: SYSTEM_TIME — встроенный видеорегистратор
Некоторые современные базы данных умеют вести историю сами, без всяких таблиц-клонов и триггеров. Достаточно один раз включить функцию — и база начнёт запоминать, когда каждая строка появилась и когда изменилась.
После этого можно буквально «заглянуть в прошлое» одной командой:
SELECT * FROM Rooms FOR SYSTEM_TIME AS OF '2026-01-01';
Эта строка говорит: «Покажи мне все комнаты в том виде, в каком они были 1 января 2026 года». Идеально для отладки: можно увидеть точное состояние данных в ту секунду, когда пользователь словил баг. Если нужна серьёзная аналитика и «путешествия во времени» — это лучший выбор.
Но есть нюанс: SYSTEM_TIME поддерживают не все базы данных. В PostgreSQL и MariaDB это работает, а вот в MySQL — нет.
3️⃣ Вариант 3: История через JSONB-слепки
Идея простая: каждый раз, когда строка меняется, мы берём её текущую версию, упаковываем целиком в один JSON-объект и сохраняем в отдельную таблицу. Если комнату не трогали полгода, ни одной новой записи за это время не появится.
Допустим, у комнаты №42 три раза менялась цена. В таблице истории будет:
room snapshot changed_at
42 {"price": 300, "status": "active"} 1 января
42 {"price": 400, "status": "active"} 15 марта
42 {"price": 500, "status": "paused"} 10 апреля
Хотим узнать цену в феврале? Берём последнюю запись до февраля — 300.
Почему JSON, а не обычная таблица-клон? Потому что JSON — это «резиновый» контейнер. Неважно, сколько новых колонок добавили в основную таблицу — JSON примет их все без единого изменения в таблице истории. Та самая проблема Schema Drift из первого варианта здесь просто не существует.
Ложка дёгтя: история раздувает базу
Об этом забывают чаще всего. Если данные меняются часто, таблица истории за полгода легко вырастает до сотен гигабайт.
Проверенная стратегия — разделять данные по «температуре»:
🔹«Горячая» история за последние 3 месяца — живёт в основной базе (PostgreSQL), к ней обращаются постоянно.
🔹 Всё, что старше — уезжает в «холодное» хранилище (ClickHouse, S3). Там место дешевле, а аналитика по миллионам старых строк работает даже быстрее.
Итого, что выбрать:
🔹 Простая система, структура не меняется → Таблица-клон с триггером
🔹 Нужна точная аналитика и «путешествие во времени» → SYSTEM_TIME
🔹 Структура часто меняется, средний масштаб → JSONB-слепки
И в любом случае — заранее решите, куда архивировать историю через год. Потом будет поздно.
اکنون در دسترس! پژوهش تلگرام ۲۰۲۵ — مهمترین بینشهای سال 
