Как устроены хранилища MySQL: InnoDB vs MyISAM vs MEMORY 🏗️
Когда вы создаёте таблицу в MySQL, у неё есть так называемый движок хранения (storage engine). Это то, как MySQL управляет данными внутри таблицы: где их хранить, как их обновлять и как быстро к ним обращаться.
В MySQL есть несколько движков, но самые популярные:
🔹InnoDB — стандартный и самый надёжный вариант 🔥
🔹MyISAM — старый, быстрый, но без транзакций ⚡
🔹MEMORY — для супербыстрых временных таблиц 🧠
Давайте разберёмся, в чём их различия и когда какой использовать.
🔥 InnoDB — современный стандарт
InnoDB — это основной и рекомендуемый движок в MySQL. Он поддерживает транзакции, обеспечивает целостность данных и отлично подходит для больших нагрузок.
Основные особенности:
✅ Поддержка транзакций — если что-то пошло не так, можно откатить изменения (ROLLBACK).
✅ Поддержка внешних ключей — можно строить связи между таблицами (FOREIGN KEY).
✅ Блокировка на уровне строк — если обновляется одна строка, другие остаются доступными.
✅ Хранит данные в кластеризованном индексе — быстрее работает с PRIMARY KEY.
🛠 Где используется?
🔹В интернет-магазинах, CRM-системах, SaaS-проектах.
🔹Там, где важна целостность данных (например, банковские операции).
🔹Если нужна высокая скорость чтения и записи одновременно.
⚡ MyISAM — быстрый, но устаревший
MyISAM был основным движком в MySQL до версии 5.5. Он работает быстрее, но не поддерживает транзакции.
Основные особенности:
✅ Очень быстрое чтение данных — идеален для аналитики.
✅ Легче в администрировании — структура проще, чем у InnoDB.
❌ Минусы:
🔹 Нет транзакций — если сервер упадёт во время записи, можно потерять данные.
🔹 Блокировка на уровне таблицы — если обновляется одна строка, вся таблица заблокирована.
🔹 Нет внешних ключей — нельзя строить сложные связи между таблицами.
🛠 Где используется?
🔹В системах, где важнее скорость чтения, а не записи (например, блоги, статистика).
🔹В проектах, где потеря данных не критична.
🔹Сейчас почти не используется, потому что InnoDB во многом его заменил.
🧠 MEMORY — супербыстрые временные таблицы
MEMORY хранит данные не на диске, а в оперативной памяти (RAM). Это делает его невероятно быстрым, но с одной важной особенностью: при перезапуске MySQL все данные исчезают!
Основные особенности:
✅ Молниеносная скорость — идеально для временных данных.
✅ Нет нагрузки на диск — всё работает в оперативке.
✅ Отлично подходит для кэша — можно временно хранить результаты сложных запросов.
❌ Минусы:
🔹Данные исчезают при рестарте MySQL.
🔹Не поддерживает TEXT и BLOB (нельзя хранить большие строки и файлы).
🔹Ограничение на размер — зависит от объёма оперативной памяти.
🛠 Где используется?
🔹Временные таблицы для отчётов.
🔹Кэширование данных, которые часто запрашиваются.
🔹Для промежуточных вычислений.
🏆 Какой движок выбрать?
🔹Если не знаете, что выбрать — берите InnoDB. Он безопасный, поддерживает транзакции и гибкий.
🔹MyISAM хорош только для устаревших проектов, но сейчас почти не используется.
🔹MEMORY подходит для временных данных, если они не должны сохраняться после перезапуска сервера.
Теперь вы знаете, как устроены хранилища MySQL и какой движок выбрать для своего проекта! 🚀
Когда вы создаёте таблицу в MySQL, у неё есть так называемый движок хранения (storage engine). Это то, как MySQL управляет данными внутри таблицы: где их хранить, как их обновлять и как быстро к ним обращаться.
В MySQL есть несколько движков, но самые популярные:
🔹InnoDB — стандартный и самый надёжный вариант 🔥
🔹MyISAM — старый, быстрый, но без транзакций ⚡
🔹MEMORY — для супербыстрых временных таблиц 🧠
Давайте разберёмся, в чём их различия и когда какой использовать.
🔥 InnoDB — современный стандарт
InnoDB — это основной и рекомендуемый движок в MySQL. Он поддерживает транзакции, обеспечивает целостность данных и отлично подходит для больших нагрузок.
Основные особенности:
✅ Поддержка транзакций — если что-то пошло не так, можно откатить изменения (ROLLBACK).
✅ Поддержка внешних ключей — можно строить связи между таблицами (FOREIGN KEY).
✅ Блокировка на уровне строк — если обновляется одна строка, другие остаются доступными.
✅ Хранит данные в кластеризованном индексе — быстрее работает с PRIMARY KEY.
🛠 Где используется?
🔹В интернет-магазинах, CRM-системах, SaaS-проектах.
🔹Там, где важна целостность данных (например, банковские операции).
🔹Если нужна высокая скорость чтения и записи одновременно.
⚡ MyISAM — быстрый, но устаревший
MyISAM был основным движком в MySQL до версии 5.5. Он работает быстрее, но не поддерживает транзакции.
Основные особенности:
✅ Очень быстрое чтение данных — идеален для аналитики.
✅ Легче в администрировании — структура проще, чем у InnoDB.
❌ Минусы:
🔹 Нет транзакций — если сервер упадёт во время записи, можно потерять данные.
🔹 Блокировка на уровне таблицы — если обновляется одна строка, вся таблица заблокирована.
🔹 Нет внешних ключей — нельзя строить сложные связи между таблицами.
🛠 Где используется?
🔹В системах, где важнее скорость чтения, а не записи (например, блоги, статистика).
🔹В проектах, где потеря данных не критична.
🔹Сейчас почти не используется, потому что InnoDB во многом его заменил.
🧠 MEMORY — супербыстрые временные таблицы
MEMORY хранит данные не на диске, а в оперативной памяти (RAM). Это делает его невероятно быстрым, но с одной важной особенностью: при перезапуске MySQL все данные исчезают!
Основные особенности:
✅ Молниеносная скорость — идеально для временных данных.
✅ Нет нагрузки на диск — всё работает в оперативке.
✅ Отлично подходит для кэша — можно временно хранить результаты сложных запросов.
❌ Минусы:
🔹Данные исчезают при рестарте MySQL.
🔹Не поддерживает TEXT и BLOB (нельзя хранить большие строки и файлы).
🔹Ограничение на размер — зависит от объёма оперативной памяти.
🛠 Где используется?
🔹Временные таблицы для отчётов.
🔹Кэширование данных, которые часто запрашиваются.
🔹Для промежуточных вычислений.
🏆 Какой движок выбрать?
🔹Если не знаете, что выбрать — берите InnoDB. Он безопасный, поддерживает транзакции и гибкий.
🔹MyISAM хорош только для устаревших проектов, но сейчас почти не используется.
🔹MEMORY подходит для временных данных, если они не должны сохраняться после перезапуска сервера.
Теперь вы знаете, как устроены хранилища MySQL и какой движок выбрать для своего проекта! 🚀
1👍15❤3🔥3🤩1
Event Scheduler в MySQL: что это и как его использовать? ⏰
Event Scheduler — это механизм MySQL, который позволяет автоматически выполнять SQL-запросы в заданное время или с определенной периодичностью. Это аналог CRON, но работает внутри MySQL.
📌 Применение Event Scheduler:
✅ Очистка старых данных (логов, временных записей)🗑️
✅ Автоматическое обновление данных📊
✅ Генерация отчетов и статистики📈
✅ Автоматическое создание резервных копий 💾
🔹 Как включить Event Scheduler?
По умолчанию Event Scheduler может быть выключен. Проверить его статус можно так:
Если он OFF, включаем его:
🔹 Создание событий в MySQL
Простейший пример однократного события:
💡 Это событие запустится один раз через 1 час и удалит старые логи.
🔹 Периодическое выполнение событий
Если нужно выполнять задачу регулярно, используем EVERY:
Это событие будет обновлять таблицу каждый день.
🔹 Управление событиями
📌 Посмотреть все события в базе:
📌 Остановить событие:
📌 Запустить снова:
📌 Удалить событие:
🔹 Что важно помнить?
1️⃣ События привязаны к БД, где они созданы.
Если сделать USE другая_БД, событие не будет видно.
2️⃣ События выполняются от имени пользователя, который их создал, с его правами.
3️⃣ Если сервер MySQL перезапустится, события сохраняются, но Event Scheduler может выключиться — проверяйте его статус! ✅
Event Scheduler — это механизм MySQL, который позволяет автоматически выполнять SQL-запросы в заданное время или с определенной периодичностью. Это аналог CRON, но работает внутри MySQL.
📌 Применение Event Scheduler:
✅ Очистка старых данных (логов, временных записей)
✅ Автоматическое обновление данных
✅ Генерация отчетов и статистики
✅ Автоматическое создание резервных копий 💾
🔹 Как включить Event Scheduler?
По умолчанию Event Scheduler может быть выключен. Проверить его статус можно так:
SHOW VARIABLES LIKE 'event_scheduler';
Если он OFF, включаем его:
SET GLOBAL event_scheduler = ON;
🔹 Создание событий в MySQL
Простейший пример однократного события:
CREATE EVENT delete_old_logs
ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 HOUR
DO
DELETE FROM logs WHERE created_at < NOW() - INTERVAL 30 DAY;
💡 Это событие запустится один раз через 1 час и удалит старые логи.
🔹 Периодическое выполнение событий
Если нужно выполнять задачу регулярно, используем EVERY:
CREATE EVENT update_stats
ON SCHEDULE EVERY 1 DAY
DO
UPDATE statistics SET last_updated = NOW();
Это событие будет обновлять таблицу каждый день.
🔹 Управление событиями
📌 Посмотреть все события в базе:
SHOW EVENTS;
📌 Остановить событие:
ALTER EVENT update_stats DISABLE;
📌 Запустить снова:
ALTER EVENT update_stats ENABLE;
📌 Удалить событие:
DROP EVENT delete_old_logs;
🔹 Что важно помнить?
1️⃣ События привязаны к БД, где они созданы.
Если сделать USE другая_БД, событие не будет видно.
2️⃣ События выполняются от имени пользователя, который их создал, с его правами.
3️⃣ Если сервер MySQL перезапустится, события сохраняются, но Event Scheduler может выключиться — проверяйте его статус! ✅
Please open Telegram to view this post
VIEW IN TELEGRAM
2👍18❤4🔥2
Как хранить бинарные данные (BLOB) в MySQL? 🔒
BLOB (Binary Large Object) — это способ хранить двоичные данные (файлы, изображения, документы и т.д.) прямо в базе MySQL. Однако есть нюансы, которые важно учитывать.
🔹 Виды BLOB:
1️⃣ TINYBLOB — до 255 байт
2️⃣ BLOB — до ~65 КБ
3️⃣ MEDIUMBLOB — до ~16 МБ
4️⃣ LONGBLOB — до ~4 ГБ
🔹 Когда стоит использовать BLOB?
✅ Для небольших файлов (например, аватары пользователей)
✅ Когда важно хранить всё в одной базе (цельность и транзакции)
✅ При отсутствии внешнего хранилища
⚠️ Большие файлы (видео, большие документы) лучше хранить вне БД, а в базе — только пути к ним.
🔹 Пример таблицы:
🔹 Вставка данных:
Или передача через параметризованный запрос из приложения (PHP/Python), где двоичные данные вставляются напрямую.
🔹 Ограничения и подводные камни:
1️⃣ Большие BLOB замедляют запросы и увеличивают размер бэкапа
2️⃣ Нужны корректные настройки (max_allowed_packet, innodb_log_file_size и др.)
3️⃣ При блокировке таблицы с большим BLOB транзакции могут зависать дольше
4️⃣ Бэкапы и восстановление займут больше времени
🔹 Рекомендации:
1️⃣ Для больших файлов — отдельное хранилище (например, S3), а в БД только ссылки
2️⃣ При использовании BLOB — ограничивать максимальный размер файлов
3️⃣ Следить за правами (функция LOAD_FILE требует соответствующих привилегий)
4️⃣ Использовать кэширование (CDN), если часто выгружаете большие файлы
BLOB (Binary Large Object) — это способ хранить двоичные данные (файлы, изображения, документы и т.д.) прямо в базе MySQL. Однако есть нюансы, которые важно учитывать.
🔹 Виды BLOB:
1️⃣ TINYBLOB — до 255 байт
2️⃣ BLOB — до ~65 КБ
3️⃣ MEDIUMBLOB — до ~16 МБ
4️⃣ LONGBLOB — до ~4 ГБ
🔹 Когда стоит использовать BLOB?
✅ Для небольших файлов (например, аватары пользователей)
✅ Когда важно хранить всё в одной базе (цельность и транзакции)
✅ При отсутствии внешнего хранилища
⚠️ Большие файлы (видео, большие документы) лучше хранить вне БД, а в базе — только пути к ним.
🔹 Пример таблицы:
CREATE TABLE user_avatars (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
avatar MEDIUMBLOB NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
🔹 Вставка данных:
INSERT INTO user_avatars (user_id, avatar)
VALUES (123, LOAD_FILE('/path/to/avatar.png'));
Или передача через параметризованный запрос из приложения (PHP/Python), где двоичные данные вставляются напрямую.
🔹 Ограничения и подводные камни:
1️⃣ Большие BLOB замедляют запросы и увеличивают размер бэкапа
2️⃣ Нужны корректные настройки (max_allowed_packet, innodb_log_file_size и др.)
3️⃣ При блокировке таблицы с большим BLOB транзакции могут зависать дольше
4️⃣ Бэкапы и восстановление займут больше времени
🔹 Рекомендации:
1️⃣ Для больших файлов — отдельное хранилище (например, S3), а в БД только ссылки
2️⃣ При использовании BLOB — ограничивать максимальный размер файлов
3️⃣ Следить за правами (функция LOAD_FILE требует соответствующих привилегий)
4️⃣ Использовать кэширование (CDN), если часто выгружаете большие файлы
👍15❤3🔥3
Часовые пояса в MySQL: как не запутаться и правильно хранить время? 🕒
Представьте: вы сохранили в базе данных время, когда пользователь отправил сообщение, а позже оказалось, что для одного пользователя это "11:00", а для другого – "14:00". Почему так происходит? Дело в часовых поясах.
🌍 Что такое UTC?
UTC (Coordinated Universal Time) – это универсальное мировое время, от которого отсчитываются все часовые пояса планеты. Это удобная точка отсчёта, которая не зависит от часового пояса вашего сервера или пользователей.
❗ Почему это важно?
Если вы будете хранить данные в разных часовых поясах (например, местное время каждого пользователя), в базе данных появится хаос. Станет сложно сравнивать даты и время, а также понять, когда именно произошло событие.
🔹 Как правильно хранить время в MySQL?
Правило простое:
1️⃣ Храните всё время в базе данных в формате UTC.
2️⃣ Конвертируйте его в местное время только тогда, когда показываете пользователю.
Для хранения времени в MySQL можно использовать разные типы данных. Наиболее распространены TIMESTAMP и DATETIME. Давайте разберёмся, какой тип лучше выбрать.
🔹 Разница между типами TIMESTAMP и DATETIME
🔘 TIMESTAMP автоматически сохраняет данные в UTC и при чтении может автоматически конвертировать в текущий часовой пояс сервера.
🔘 DATETIME не имеет привязки к часовому поясу и хранит время ровно так, как вы его сохранили. Поэтому при использовании DATETIME нужно всегда явно указывать UTC-время.
🔹 Пример работы с UTC:
✅ Сохраняем текущее время в UTC:
✅ Читаем данные и переводим в нужный пояс (например, для Москвы, +03:00)
🔹 Итоговые рекомендации:
1️⃣ Используйте TIMESTAMP для автоматического хранения данных в UTC.
2️⃣ Для ручного управления используйте тип DATETIME и всегда сохраняйте в UTC.
3️⃣ Переводите данные в локальное время только при отображении.
Представьте: вы сохранили в базе данных время, когда пользователь отправил сообщение, а позже оказалось, что для одного пользователя это "11:00", а для другого – "14:00". Почему так происходит? Дело в часовых поясах.
🌍 Что такое UTC?
UTC (Coordinated Universal Time) – это универсальное мировое время, от которого отсчитываются все часовые пояса планеты. Это удобная точка отсчёта, которая не зависит от часового пояса вашего сервера или пользователей.
❗ Почему это важно?
Если вы будете хранить данные в разных часовых поясах (например, местное время каждого пользователя), в базе данных появится хаос. Станет сложно сравнивать даты и время, а также понять, когда именно произошло событие.
🔹 Как правильно хранить время в MySQL?
Правило простое:
1️⃣ Храните всё время в базе данных в формате UTC.
2️⃣ Конвертируйте его в местное время только тогда, когда показываете пользователю.
Для хранения времени в MySQL можно использовать разные типы данных. Наиболее распространены TIMESTAMP и DATETIME. Давайте разберёмся, какой тип лучше выбрать.
🔹 Разница между типами TIMESTAMP и DATETIME
🔘 TIMESTAMP автоматически сохраняет данные в UTC и при чтении может автоматически конвертировать в текущий часовой пояс сервера.
🔘 DATETIME не имеет привязки к часовому поясу и хранит время ровно так, как вы его сохранили. Поэтому при использовании DATETIME нужно всегда явно указывать UTC-время.
🔹 Пример работы с UTC:
✅ Сохраняем текущее время в UTC:
INSERT INTO messages (sent_at)
VALUES (UTC_TIMESTAMP());
✅ Читаем данные и переводим в нужный пояс (например, для Москвы, +03:00)
SELECT sent_at,
CONVERT_TZ(sent_at, '+00:00', '+03:00') AS sent_at_moscow
FROM messages;
🔹 Итоговые рекомендации:
1️⃣ Используйте TIMESTAMP для автоматического хранения данных в UTC.
2️⃣ Для ручного управления используйте тип DATETIME и всегда сохраняйте в UTC.
3️⃣ Переводите данные в локальное время только при отображении.
1👍20❤5🔥5
NULL в SQL: зачем знать и как работать
Что такое NULL?
Это специальное значение «неизвестно / отсутствует». Оно не равно нулю, пустой строке или 0 — это «ничего».
Почему NULL может создать проблемы
🔹 Сравнение
🔹Агрегаты (`SUM`, `AVG`) игнорируют NULL → итоги оказываются заниженными.
🔹Деление на 0 вызывает ошибку, если предварительно не обработать значение.
В MySQL есть три базовые функции, которые помогают держать ситуацию под контролем.
1️⃣ IFNULL(expr, alt) — поставить значение по умолчанию
Возвращает
Результат: вместо NULL-email выводится строка-заглушка.
2️⃣ COALESCE(expr1, expr2, …) — взять первое ненулевое
Последовательно проверяет аргументы слева направо и возвращает первый, который не NULL.
Сценарий: если email отсутствует, берём phone; если и он NULL — выводим фразу «нет контактов».
3️⃣ NULLIF(a, b) — обнулить при равенстве
Возвращает NULL, когда a = b, иначе возвращает a. Полезно, чтобы избежать деления на ноль.
Если orders_count равен 0, деление не выполняется — результатом будет NULL вместо ошибки.
Памятка
🔹Для проверок используйте IS NULL и IS NOT NULL, а не = NULL.
🔹Проверяйте, как агрегатные функции ведут себя с пропущенными данными.
🔹В формулах страхуйтесь NULLIF, чтобы не получить «division by zero».
Запомните IFNULL, COALESCE и NULLIF — с ними работа с NULL становится предсказуемой и безопасной. 🚀
Что такое NULL?
Это специальное значение «неизвестно / отсутствует». Оно не равно нулю, пустой строке или 0 — это «ничего».
Почему NULL может создать проблемы
🔹 Сравнение
= NULL
всегда возвращает FALSE → условия «теряют» строки. 🔹Агрегаты (`SUM`, `AVG`) игнорируют NULL → итоги оказываются заниженными.
🔹Деление на 0 вызывает ошибку, если предварительно не обработать значение.
В MySQL есть три базовые функции, которые помогают держать ситуацию под контролем.
1️⃣ IFNULL(expr, alt) — поставить значение по умолчанию
Возвращает
alt
, если expr
равно NULL, иначе возвращает expr
.
SELECT name,
IFNULL(email, 'no-email@example.com') AS contact
FROM users;
Результат: вместо NULL-email выводится строка-заглушка.
2️⃣ COALESCE(expr1, expr2, …) — взять первое ненулевое
Последовательно проверяет аргументы слева направо и возвращает первый, который не NULL.
SELECT name,
COALESCE(email, phone, 'нет контактов') AS main_contact
FROM users;
Сценарий: если email отсутствует, берём phone; если и он NULL — выводим фразу «нет контактов».
3️⃣ NULLIF(a, b) — обнулить при равенстве
Возвращает NULL, когда a = b, иначе возвращает a. Полезно, чтобы избежать деления на ноль.
SELECT revenue / NULLIF(orders_count, 0) AS avg_check
FROM shop_stats;
Если orders_count равен 0, деление не выполняется — результатом будет NULL вместо ошибки.
Памятка
🔹Для проверок используйте IS NULL и IS NOT NULL, а не = NULL.
🔹Проверяйте, как агрегатные функции ведут себя с пропущенными данными.
🔹В формулах страхуйтесь NULLIF, чтобы не получить «division by zero».
Запомните IFNULL, COALESCE и NULLIF — с ними работа с NULL становится предсказуемой и безопасной. 🚀
1❤32👍23🔥9
CHECK-ограничения в MySQL: валидация прямо в таблице ✅
Когда вы хотите убедиться, что в таблицу попадают только корректные данные — не всегда нужно писать сложную логику в приложении. Иногда достаточно встроенного механизма MySQL: ограничения CHECK.
🔹 Что такое CHECK?
Это правило, которое автоматически проверяет значение в колонке при вставке (INSERT) или обновлении (UPDATE). Если правило нарушено — операция отменяется с ошибкой.
Пример:
Теперь нельзя вставить пользователя с возрастом -5 или 999 — MySQL просто не даст это сделать.
Синтаксис CHECK
🔸 Можно добавлять при создании таблицы (CREATE TABLE) или позже (ALTER TABLE).
🔸 Можно использовать AND, OR, арифметику, сравнения, функции (с ограничениями).
🔸 Поддерживается в MySQL 8.0+. В старых версиях CHECK игнорировался (!).
Пример с условием на строку:
Здесь мы запрещаем отрицательные цены.
Добавление ограничения в существующую таблицу:
Что произойдёт при нарушении?
⚠️ Важно помнить:
🔹CHECK срабатывает только для новых данных — старые строки не проверяются.
🔹Если значение NULL, правило обычно не нарушается (NULL считается «неизвестным» и не сравнивается напрямую).
🔹Ошибку можно перехватывать в приложении, чтобы сообщить пользователю.
✅ Когда стоит использовать CHECK:
🔹 Для ограничений: возраст, положительная цена, длина строки, формат (через LIKE или REGEXP).
🔹Когда хотите, чтобы в таблице «по умолчанию» всегда были только корректные данные.
🔹Чтобы упростить валидацию и сделать БД самодостаточной (например, при работе с внешними источниками данных).
Когда вы хотите убедиться, что в таблицу попадают только корректные данные — не всегда нужно писать сложную логику в приложении. Иногда достаточно встроенного механизма MySQL: ограничения CHECK.
🔹 Что такое CHECK?
Это правило, которое автоматически проверяет значение в колонке при вставке (INSERT) или обновлении (UPDATE). Если правило нарушено — операция отменяется с ошибкой.
Пример:
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).
🔹Когда хотите, чтобы в таблице «по умолчанию» всегда были только корректные данные.
🔹Чтобы упростить валидацию и сделать БД самодостаточной (например, при работе с внешними источниками данных).
👍24❤2🔥2
AUTO_INCREMENT в MySQL: как работает, можно ли переопределить и сбросить счётчик? 🔢
💬 Почему важно
Вопрос про AUTO_INCREMENT регулярно всплывает на собеседованиях: «почему ID прыгают через 5?», «как начать нумерацию с 1000?», «можно вернуть счётчик обратно?». Разберёмся на практике.
🔹 Что делает AUTO_INCREMENT
При каждой вставке (`INSERT`) MySQL берёт текущее значение счётчика, увеличивает его на 1 и записывает в колонку.
🔹 Как задать стартовое значение
Нужно указать AUTO_INCREMENT = n при создании или изменить уже существующую таблицу:
💡 Полезно, если хотите «красивые» ID или переносите старые данные.
🔹 Почему появляются «дырки» в нумерации
1. DELETE. Удалили строку с id = 5 → число 5 уже не вернётся.
2. ROLLBACK. Транзакция вставила id = 6 и откатилась — 6 пропадает.
3. Сбой сервера. MySQL резервирует диапазон ID в буфере; при крэше часть чисел «теряется».
🔹 Можно ли сбросить счётчик?
Да, но осторожно.
Условие: новое значение должно быть больше любого существующего id. Иначе получите ошибку или конфликт PK.
🛑 На проде почти никогда не делают — потеря сквозной уникальности мешает логам и внешним ссылкам.
🔹 Грабли и советы
1️⃣ Не используйте AUTO_INCREMENT как «количество строк» — после DELETE счётчик не отражает реальное число записей.
2️⃣ Храните ссылки на записи только по уникальным id, а не позициям.
3️⃣ Если важно отсутствие дырок (к примеру, номера чеков) — создавайте отдельную таблицу «генератор последовательностей» или используйте сериализацию в приложении.
📝 Итог
1️⃣ Начать с нужного числа — AUTO_INCREMENT = N.
2️⃣ Сбросить можно, но не ниже текущего максимума.
3️⃣ Пропуски после DELETE и ROLLBACK — нормальное поведение.
💬 Почему важно
Вопрос про AUTO_INCREMENT регулярно всплывает на собеседованиях: «почему ID прыгают через 5?», «как начать нумерацию с 1000?», «можно вернуть счётчик обратно?». Разберёмся на практике.
🔹 Что делает AUTO_INCREMENT
При каждой вставке (`INSERT`) MySQL берёт текущее значение счётчика, увеличивает его на 1 и записывает в колонку.
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100)
);
🔹 Как задать стартовое значение
Нужно указать AUTO_INCREMENT = n при создании или изменить уже существующую таблицу:
-- сразу при создании
CREATE TABLE orders (
id INT AUTO_INCREMENT PRIMARY KEY,
product VARCHAR(100)
) AUTO_INCREMENT = 1000;
-- позднее
ALTER TABLE orders AUTO_INCREMENT = 5000;
💡 Полезно, если хотите «красивые» ID или переносите старые данные.
🔹 Почему появляются «дырки» в нумерации
1. DELETE. Удалили строку с id = 5 → число 5 уже не вернётся.
2. ROLLBACK. Транзакция вставила id = 6 и откатилась — 6 пропадает.
3. Сбой сервера. MySQL резервирует диапазон ID в буфере; при крэше часть чисел «теряется».
🔹 Можно ли сбросить счётчик?
Да, но осторожно.
ALTER TABLE users AUTO_INCREMENT = 1;
Условие: новое значение должно быть больше любого существующего id. Иначе получите ошибку или конфликт PK.
🛑 На проде почти никогда не делают — потеря сквозной уникальности мешает логам и внешним ссылкам.
🔹 Грабли и советы
1️⃣ Не используйте AUTO_INCREMENT как «количество строк» — после DELETE счётчик не отражает реальное число записей.
2️⃣ Храните ссылки на записи только по уникальным id, а не позициям.
3️⃣ Если важно отсутствие дырок (к примеру, номера чеков) — создавайте отдельную таблицу «генератор последовательностей» или используйте сериализацию в приложении.
📝 Итог
1️⃣ Начать с нужного числа — AUTO_INCREMENT = N.
2️⃣ Сбросить можно, но не ниже текущего максимума.
3️⃣ Пропуски после DELETE и ROLLBACK — нормальное поведение.
🔥15❤4👍2🗿1
Python — это ключ к аналитике, автоматизации и быстрой разработке. Мы собрали материал, который поможет заложить прочный фундамент и сразу перейти к практике.
Что внутри:
1️⃣ стартуем в формате «Ранний доступ» — уроки уже открыты и будут регулярно пополняться;
2️⃣ практические задания после каждого модуля, чтобы теория превращалась в навык;
3️⃣ приятные сюрпризы и новые фичи впереди!
💡 Основной контент и задания — бесплатно.
Учитесь без ограничений и прокачивайте навыки вместе с нами.
Хотите ещё глубже?
Подключайте Премиум, чтобы решать задачи с реальных собеседований в топ-IT-компаниях и получать расширенные возможности курса.
Перейти к курсу Python Academy → https://python-academy.org/
Чтобы не пропустить обновления и бонусы, присоединяйтесь к нашему Telegram-каналу 👉 https://t.me/pythonacademyofficial
Спасибо, что растёте вместе с SQL Academy — и теперь с Python Academy. Ваши успехи — наша радость! 💙
Please open Telegram to view this post
VIEW IN TELEGRAM
python-academy.org
Interactive Online Python Course — Python Academy
Interactive online Python course with exercises and tasks for writing Python code. Perfect for beginner analysts, developers, and testers!
🔥59❤14👍5
CASE в ORDER BY: сортируем по условию 🔄
Иногда нужно показать записи не просто «по алфавиту» или «по дате», а сначала важные, потом второстепенные. В MySQL (и в SQL вообще) это делается через выражение CASE прямо в ORDER BY.
💡 Зачем знать?
Вопрос часто звучит на собеседованиях: «Как вывести VIP‑клиентов сверху, а остальных — по имени?» Умение написать условную сортировку показывает, что вы понимаете SQL‑движок, а не только копируете примеры.
🔹 Базовый пример: VIP‑клиенты выше остальных
▶️ CASE возвращает 0 для VIP, 1 — для всех остальных.
▶️ В порядке сортировки 0 идёт раньше 1, поэтому VIP‑ы будут первыми
🔹Сложнее: приоритеты A > B > C → остальные
▶️ Сначала все со статусом A, потом B, затем C, затем остальные.
▶️ Внутри каждой группы — свежие заказы выше (по дате).
Типичные ошибки ⚠️
1️⃣ NULL‑ы: если столбец может быть NULL, добавьте явную проверку
2️⃣ Числа вместо строк: не забывайте, что CASE должен возвращать однотипные значения (в примерах — всегда INT).
3️⃣ Производительность: выражение в ORDER BY отключает использование индекса.
Для больших таблиц можно создать сгенерированный столбец с тем же CASE и проиндексировать его.
Итоги ✅
🔸CASE в ORDER BY даёт гибкую условную сортировку без подзапросов.
🔸Легко расставить приоритеты: VIP, скидки, статусы…
🔸Следите за NULL‑ами и индексацией при больших объёмах.
Иногда нужно показать записи не просто «по алфавиту» или «по дате», а сначала важные, потом второстепенные. В MySQL (и в SQL вообще) это делается через выражение CASE прямо в ORDER BY.
💡 Зачем знать?
Вопрос часто звучит на собеседованиях: «Как вывести VIP‑клиентов сверху, а остальных — по имени?» Умение написать условную сортировку показывает, что вы понимаете SQL‑движок, а не только копируете примеры.
🔹 Базовый пример: VIP‑клиенты выше остальных
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‑ами и индексацией при больших объёмах.
🔥25👍15❤9
LIMIT + OFFSET и «вечная прокрутка»: как делают Instagram-ленты 📜➡️
Зачем знать?
На собеседованиях часто просят: «Сделай API, чтобы лента грузилась порциями». Понимание пагинации важно и для новичков — вы сможете писать быстрые, масштабируемые запросы.
────────────────────────
1️⃣ Классический способ — LIMIT + OFFSET
Работает просто, но есть три проблемы:
🔹 OFFSET N сначала пропускает N строк → чем дальше, тем медленнее.
🔹 Если за время прокрутки добавятся новые посты, пользователь увидит дубликаты или пропуски.
🔹При больших значениях OFFSET сервер читает тысячи строк зря.
2️⃣ Key-set pagination (a.k.a. «лента без дыр»)
Идея: вместо «пропустить N» передаём последний id/дату уже загруженной записи и берём следующие сверху.
Плюсы:
✅ Нет медленного OFFSET — используется индекс по created_at.
✅ Новые посты не «вставляются» в уже просмотренную часть, дубликатов нет.
✅ Маркер-курсор можно прятать в URL или JSON‐ответе.
3️⃣ Что передавать в качестве курсора?
🔹 Авто-инкремент id — легко, но если бывают «дыры» (удаления) — используйте < 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. Результат удивит! 🚀
Зачем знать?
На собеседованиях часто просят: «Сделай API, чтобы лента грузилась порциями». Понимание пагинации важно и для новичков — вы сможете писать быстрые, масштабируемые запросы.
────────────────────────
1️⃣ Классический способ — LIMIT + OFFSET
-- страница 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. Результат удивит! 🚀
👍26🔥18❤15
WINDOW-функция NTILE(): делим пользователей на квартели 📊
На собеседованиях любят задачу «раскидай пользователей по квартилям по количеству покупок».
NTILE() решает её одной строкой, без подзапросов и циклов в приложении.
1️⃣ Что такое NTILE()
Оконная функция, которая разбивает отсортированный набор строк на N равных «коробок» (tiles).
🔹 N — число групп (4 = квартели).
🔹 PARTITION BY (необязательно) — создаёт независимые группы (например, по стране).
🔹 ORDER BY — признак, по которому сортируем и считаем ранг.
2️⃣ Классический пример: 4 квартиля по количеству заказов
🔹 Пользователи с наибольшим orders_count попадают в quartile = 1, а с наименьшим — в quartile = 4.
🔹 Если строк не делится поровну, верхние группы получат на 1 запись больше (MySQL делит «сверху»).
3️⃣ Квартели внутри каждой страны
Теперь каждый блок «страна» делится на свои 4 части — удобно для локальных лидеров.
4️⃣ Как использовать результат
🔹Маркетинг: таргетировать акции только quartile = 4 (самые неактивные).
🔹Аналитика: сравнить средний чек между квартилями.
🔍 Потренируйтесь
Откройте готовый плейграунд, запустите NTILE(4) и поэкспериментируйте с разным числом групп.
👉 https://sqlplayground.app/sandbox/683ef210cb9fcbe8d3db3a9e
✅ Итог
🔹NTILE(N) быстро делит данные на равные сегменты.
🔹Работает вместе с PARTITION BY и любой сортировкой.
🔹Незаменим при сегментации пользователей, товаров или чеков.
На собеседованиях любят задачу «раскидай пользователей по квартилям по количеству покупок».
NTILE() решает её одной строкой, без подзапросов и циклов в приложении.
1️⃣ Что такое NTILE()
Оконная функция, которая разбивает отсортированный набор строк на N равных «коробок» (tiles).
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 и любой сортировкой.
🔹Незаменим при сегментации пользователей, товаров или чеков.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥27❤6👍5
SEQUENCE (MySQL 8.0.29+) — современная альтернатива AUTO_INCREMENT
💡 Зачем знать
🔹 На собеседованиях всё чаще спрашивают: «Чем SEQUENCE отличается от AUTO_INCREMENT и зачем он нужен?»
🔹 В проде помогает, когда один источник чисел требуется сразу нескольким таблицам или когда нельзя допускать длинной блокировки PRIMARY KEY.
1️⃣ Что такое SEQUENCE?
Это отдельный объект в схеме, хранящий текущее значение счётчика.
🔹 Не привязан к конкретной таблице.
🔹 Умеет шаг ( INCREMENT ), начальное значение (START).
🔹 Получить число можно в любом запросе: NEXT VALUE FOR seq_name.
2️⃣ Вставка чисел в таблицу
Получаем уникальный id без AUTO_INCREMENT в самой таблице.
3️⃣ Один счётчик — много таблиц
Нужно, чтобы tickets и invoices шли в общей нумерации? Просто используйте ту же последовательность:
4️⃣ Дополнительные возможности
5️⃣ Сравнение с AUTO_INCREMENT 🤔
🔹 Объект:
– SEQUENCE — отдельный объект в схеме
– AUTO_INCREMENT — жёстко привязан к таблице
🔹Один счётчик для нескольких таблиц:
– SEQUENCE — да, можно использовать в любой таблице
– AUTO_INCREMENT — нет, только внутри своей таблицы
🔹Задать старт / шаг:
– SEQUENCE — гибко: START WITH, INCREMENT BY для каждой последовательности
– AUTO_INCREMENT — глобальные AI_OFFSET / AI_INCREMENT, меньше контроля
🔹Сброс счётчика:
– SEQUENCE —
– AUTO_INCREMENT —
✅ Итог
SEQUENCE = гибкий, «шарящийся» счётчик, который:
🔹Позволяет одной командой нумеровать разные таблицы.
🔹Даёт точный контроль над шагом и стартом.
💡 Зачем знать
🔹 На собеседованиях всё чаще спрашивают: «Чем SEQUENCE отличается от AUTO_INCREMENT и зачем он нужен?»
🔹 В проде помогает, когда один источник чисел требуется сразу нескольким таблицам или когда нельзя допускать длинной блокировки PRIMARY KEY.
1️⃣ Что такое SEQUENCE?
Это отдельный объект в схеме, хранящий текущее значение счётчика.
🔹 Не привязан к конкретной таблице.
🔹 Умеет шаг ( INCREMENT ), начальное значение (START).
🔹 Получить число можно в любом запросе: NEXT VALUE FOR seq_name.
-- создаём последовательность с шага 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 = гибкий, «шарящийся» счётчик, который:
🔹Позволяет одной командой нумеровать разные таблицы.
🔹Даёт точный контроль над шагом и стартом.
🔥12❤5👍3
SUBSTRING() / LEFT() / RIGHT() — извлекаем части строки ✂️
❗ Зачем нужно
Чистка и разбор данных — частая задача аналитика. Извлечь код страны из телефона, отделить год из даты в тексте, взять первые 3 буквы артикулы — всё это делается прямо в SQL, без кода на стороне приложения.
1️⃣ SUBSTRING() — получить подстроку
2️⃣ LEFT(str, N) — «первые N символов»
3️⃣ RIGHT(str, N) — «последние N символов»
💡 Практический пример: разбираем телефон
⚠️ Подводные камни
🔹 Нумерация с 1: первый символ — позиция 1, а не 0.
🔹 NULL → NULL: любое из функций вернёт NULL, если аргумент NULL — учитывайте в отчётах.
✅ Итог
🔹SUBSTRING — универсальный нож: позиция + длина.
🔹LEFT / RIGHT — быстрый способ взять начало или конец строки.
❗ Зачем нужно
Чистка и разбор данных — частая задача аналитика. Извлечь код страны из телефона, отделить год из даты в тексте, взять первые 3 буквы артикулы — всё это делается прямо в SQL, без кода на стороне приложения.
1️⃣ SUBSTRING() — получить подстроку
-- с 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 — быстрый способ взять начало или конец строки.
👍20❤10🔥1👌1
DATE_ADD / DATE_SUB — прибавляем дни, месяцы, годы 📅
❗ Зачем знать
Практически каждый отчёт или запрос «за последние N дней» использует именно эти функции. Умение правильно прибавлять/вычитать интервалы избавит от ошибок с високосными годами и разной длиной месяцев.
1️⃣ Синтаксис
unit бывает: SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, YEAR и их комбинации (например, DAY_MICROSECOND).
2️⃣ Простые примеры
3️⃣ Кейс: отчёт «за последние 30 дней»
👉 CURDATE() = 00:00:00 текущего дня, поэтому попадут 30 «полных» дней.
4️⃣ Прибавляем месяцы правильно
DATE_ADD учитывает разную длину месяцев и високосные годы:
⚠️ Подводные камни
1. Точность NOW(): если нужен только день без времени — используйте CURDATE().
2. Интервалы с недельной гранулярностью: INTERVAL 1 WEEK = 7 дней, не календарная неделя.
❗ Зачем знать
Практически каждый отчёт или запрос «за последние N дней» использует именно эти функции. Умение правильно прибавлять/вычитать интервалы избавит от ошибок с високосными годами и разной длиной месяцев.
1️⃣ Синтаксис
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 дней, не календарная неделя.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤18🔥9👍4
ELT() и FIELD() — превращаем числовой код в текст без JOIN 🏷️
❓ Зачем нужно
Частый кейс: в таблице хранится числовой статус (1 = “pending”, 2 = “done”, 3 = “failed”).
Обычно делают JOIN с маленьким справочником, но MySQL умеет отдать текст прямо в SELECT — скомпилируем «карманный словарь» функциями FIELD() + ELT().
1️⃣ FIELD(needle, list…) — возвращает позицию элемента
2️⃣ ELT(index, list…) — берёт элемент по номеру
💡 Объединяем: число → позиция → текст
🔹 FIELD находит позицию status_code в списке кодов,
🔹 ELT вытаскивает строку из второго списка той же длины.
3️⃣ Удобный вариант через CASE?
Да, но ELT/FIELD короче, особенно для 5-10 значений.
4️⃣ Подводные камни ⚠️
🔹 Если FIELD вернёт 0 (код не найден) — ELT() даст NULL.
🔹Порядок важен: списки в FIELD и ELT должны совпадать по длине и позиции.
✅ Итог
🔹FIELD() находит позицию кода, ELT() возвращает строку по этой позиции.
🔹Для 3-5 статусов — самый быстрый способ «раскрасить» отчёт без JOIN и CASE.
❓ Зачем нужно
Частый кейс: в таблице хранится числовой статус (1 = “pending”, 2 = “done”, 3 = “failed”).
Обычно делают JOIN с маленьким справочником, но MySQL умеет отдать текст прямо в SELECT — скомпилируем «карманный словарь» функциями FIELD() + ELT().
1️⃣ FIELD(needle, list…) — возвращает позицию элемента
SELECT FIELD(3, 1,2,3,4);
-- Результат: 3
SELECT FIELD('b', 'a','b','c');
-- Результат: 2
2️⃣ ELT(index, list…) — берёт элемент по номеру
SELECT ELT(3, 'pending','done','failed','unknown');
-- Результат: 'failed'
💡 Объединяем: число → позиция → текст
SELECT
order_id,
status_code,
ELT(
FIELD(status_code, 1,2,3,4),
'pending','in_progress','done','failed'
) AS status_text
FROM orders;
🔹 FIELD находит позицию status_code в списке кодов,
🔹 ELT вытаскивает строку из второго списка той же длины.
3️⃣ Удобный вариант через CASE?
Да, но ELT/FIELD короче, особенно для 5-10 значений.
-- аналог на CASE (длиннее)
CASE status_code
WHEN 1 THEN 'pending'
WHEN 2 THEN 'in_progress'
WHEN 3 THEN 'done'
WHEN 4 THEN 'failed'
ELSE 'unknown'
END
4️⃣ Подводные камни ⚠️
🔹 Если FIELD вернёт 0 (код не найден) — ELT() даст NULL.
🔹Порядок важен: списки в FIELD и ELT должны совпадать по длине и позиции.
✅ Итог
🔹FIELD() находит позицию кода, ELT() возвращает строку по этой позиции.
🔹Для 3-5 статусов — самый быстрый способ «раскрасить» отчёт без JOIN и CASE.
🔥20❤5👍3🥰1🙏1
🎉 PostgreSQL теперь с нами! 🐘🎉
Мы расширили движок курса — добавили полную поддержку PostgreSQL.
Теперь все уроки, задачи и автотесты работают как на MySQL, так и на PostgreSQL. Выбирайте любимый диалект — знание SQL теперь прокачивается вдвойне!🚀
Что нового:
1️⃣ Двойной режим — переключайтесь между MySQL ↔ PostgreSQL в один клик.
2️⃣ Все практические задачи проверяются в обоих СУБД, а значит больше гибкости и реальной пользы.
3️⃣ Обновлённые подсказки и примеры с учётом синтаксиса Postgres.
💡 Базовый контент и задания по-прежнему бесплатны.
Спасибо, что растёте вместе с SQL Academy. Теперь на MySQL и PostgreSQL! 💙
Мы расширили движок курса — добавили полную поддержку PostgreSQL.
Теперь все уроки, задачи и автотесты работают как на MySQL, так и на PostgreSQL. Выбирайте любимый диалект — знание SQL теперь прокачивается вдвойне!
Что нового:
1️⃣ Двойной режим — переключайтесь между MySQL ↔ PostgreSQL в один клик.
2️⃣ Все практические задачи проверяются в обоих СУБД, а значит больше гибкости и реальной пользы.
3️⃣ Обновлённые подсказки и примеры с учётом синтаксиса Postgres.
💡 Базовый контент и задания по-прежнему бесплатны.
Спасибо, что растёте вместе с SQL Academy. Теперь на MySQL и PostgreSQL! 💙
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥119👍31❤18
UUID() и UUID_SHORT() — генерируем уникальный ID без сервера приложений 🎲
❗Зачем знать
🔹Иногда нужно создать уникальный первичный ключ на стороне БД, а не приложения.
1️⃣ UUID() — стандартный 128-битный идентификатор
🔹36 символов (32 шестн. + 4 дефисa).
🔹Практически нулевая вероятность коллизии.
🔹Минусы: длинная строка → индекс ~4× крупнее, хуже кэшируется.
2️⃣ UUID_SHORT() — компактный 64-битный вариант
🔹Возвращает BIGINT UNSIGNED (до 18 цифр).
🔹Формируется как: (server_id << 48) + (unix_ts << 24) + increment.
🔹Удобен для числовых PK 👉 индекс меньше, быстрее сортировка.
🔹Риск коллизии есть при одинаковом server_id (на реплике проверьте server_id!).
3️⃣ Практический пример: таблица заказов
✅ Итог
🔹UUID() — гарантированная уникальность, но тяжёлый индекс.
🔹UUID_SHORT() — компактный числовой ID, если контролируете server_id.
🔹Оба способа позволяют генерировать ключ прямо в MySQL без внешнего сервиса или кода приложения.
❗Зачем знать
🔹Иногда нужно создать уникальный первичный ключ на стороне БД, а не приложения.
1️⃣ UUID() — стандартный 128-битный идентификатор
SELECT UUID();
-- '550e8400-e29b-41d4-a716-446655440000'
🔹36 символов (32 шестн. + 4 дефисa).
🔹Практически нулевая вероятность коллизии.
🔹Минусы: длинная строка → индекс ~4× крупнее, хуже кэшируется.
2️⃣ UUID_SHORT() — компактный 64-битный вариант
SELECT UUID_SHORT();
-- 281474976710661
🔹Возвращает BIGINT UNSIGNED (до 18 цифр).
🔹Формируется как: (server_id << 48) + (unix_ts << 24) + increment.
🔹Удобен для числовых PK 👉 индекс меньше, быстрее сортировка.
🔹Риск коллизии есть при одинаковом server_id (на реплике проверьте server_id!).
3️⃣ Практический пример: таблица заказов
CREATE TABLE orders (
id CHAR(36) DEFAULT (UUID()) PRIMARY KEY,
user_id INT,
total DECIMAL(10,2)
);
✅ Итог
🔹UUID() — гарантированная уникальность, но тяжёлый индекс.
🔹UUID_SHORT() — компактный числовой ID, если контролируете server_id.
🔹Оба способа позволяют генерировать ключ прямо в MySQL без внешнего сервиса или кода приложения.
❤20👍5
Задача из собеседования (Cian, middle): считаем, сколько раз издавалось произведение 📚
Схема:
🔹Books(id, title) — произведение
🔹BookEditions(id, book_id, publish_year, pages) — издание произведения
🔹BookCopies(id, edition_id, inventory_num) — экземпляр издания
🎯 Формулировка
Найти произведения, которые издавались более 5 раз. Вывести только название произведения (title).
🧪 Сначала решите сами!
Откройте задачу и попробуйте написать запрос без подсказок:
👉 https://sql-academy.org/ru/trainer/tasks/125
— Разбор ——
Как подойти шаг за шагом:
1️⃣ Что нужно посчитать?
Количество изданий на каждое произведение → считаем строки в BookEditions по book_id.
2️⃣ Где лежит название?
В Books. Значит, к агрегату нужно присоединить Books по Books.id = BookEditions.book_id.
3️⃣ По чему группировать?
По уникальному идентификатору книги (и для вывода — по названию): GROUP BY b.id, b.title.
4️⃣ Как отфильтровать только те, у кого >5 изданий?
Через HAVING, потому что фильтруем по агрегату (COUNT).
Правильный ответ
На что обратить внимание❗️
🔹Группируем по b.id, b.title, а не только по b.title: два разных произведения могут теоретически иметь одинаковое название; по id безопаснее.
А если в условии скрыт нюанс «издавалось в разные годы»?
Иногда собеседующий подразумевает уникальные годы издания. Тогда используйте COUNT(DISTINCT e.publish_year)
Схема:
🔹Books(id, title) — произведение
🔹BookEditions(id, book_id, publish_year, pages) — издание произведения
🔹BookCopies(id, edition_id, inventory_num) — экземпляр издания
🎯 Формулировка
Найти произведения, которые издавались более 5 раз. Вывести только название произведения (title).
🧪 Сначала решите сами!
Откройте задачу и попробуйте написать запрос без подсказок:
👉 https://sql-academy.org/ru/trainer/tasks/125
— Разбор ——
Как подойти шаг за шагом:
1️⃣ Что нужно посчитать?
Количество изданий на каждое произведение → считаем строки в BookEditions по book_id.
2️⃣ Где лежит название?
В Books. Значит, к агрегату нужно присоединить Books по Books.id = BookEditions.book_id.
3️⃣ По чему группировать?
По уникальному идентификатору книги (и для вывода — по названию): GROUP BY b.id, b.title.
4️⃣ Как отфильтровать только те, у кого >5 изданий?
Через HAVING, потому что фильтруем по агрегату (COUNT).
Правильный ответ
SELECT b.title
FROM BookEditions e
JOIN Books b ON b.id = e.book_id
GROUP BY b.id, b.title
HAVING COUNT(e.id) > 5;
На что обратить внимание
🔹Группируем по b.id, b.title, а не только по b.title: два разных произведения могут теоретически иметь одинаковое название; по id безопаснее.
А если в условии скрыт нюанс «издавалось в разные годы»?
Иногда собеседующий подразумевает уникальные годы издания. Тогда используйте COUNT(DISTINCT e.publish_year)
SELECT b.title
FROM BookEditions e
JOIN Books b ON b.id = e.book_id
GROUP BY b.id, b.title
HAVING COUNT(DISTINCT e.publish_year) > 5;
Please open Telegram to view this post
VIEW IN TELEGRAM
👍32🔥10❤9👏2👌1
Вы когда-нибудь задумывались, как правильно вычислить возраст человека в SQL?
🚫 Неправильный подход:
SELECT YEAR(NOW()) - YEAR('2003-07-03 14:10:26');
Проблема такого подхода в том, что он не учитывает был ли день рождения у данного человека в этом году или ещё нет. То есть, если на момент запроса уже наступило 3-е июля (07-03), то человек отпраздновал свой день рождения и ему уже 21 лет, иначе ему по-прежнему 20 года. Разница функций YEAR тут будет бесполезна — в обоих случаях она даст 21 лет.
SELECT FLOOR(DATEDIFF(NOW(), '2003-07-03 14:10:26') / 365);
Это лучше, но всё же не совсем точно из-за наличия високосных годов. 📆
✅ Правильный подход с TIMESTAMPDIFF:
TIMESTAMPDIFF(YEAR, '2003-07-03 14:10:26', NOW());
Используя функцию TIMESTAMPDIFF, мы получаем точную разницу в годах между двумя датами, учитывая все нюансы календаря! 🎯
Так что, когда дело доходит до вычисления возраста, доверяйте TIMESTAMPDIFF — и ваши расчёты будут точны как никогда! 🚀💡
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥43👍15❤4
⚡ ClickHouse vs Greenplum: что выбрать для аналитики?
📅 10 сентября | 20:00 мск | бесплатно
✅ На вебинаре разберёмся, какая СУБД лучше подойдёт для ваших задач:
•Архитектура, производительность и масштабируемость CH и Greenplum
•Различия в хранении и обработке данных
•Как базы показывают себя в OLAP, ETL и ad-hoc аналитике
•Реальные кейсы внедрения и оптимизации
✅ После урока вы сможете:
•Выбирать оптимальное решение под разные сценарии
•Оценивать производительность и удобство работы
•Понимать архитектурные плюсы и минусы каждой СУБД
💥 Участие бесплатное — регистрируйтесь и приходите: [https://otus.pw/SoTpx/?erid=2W5zFJEjnp8]
Бесплатное занятие приурочено к старту курса ClickHouse. После обучения вы научитесь быстро и эффективно настраивать БД, работать с ее продвинутыми функциями, интегрировать с другими системами и выбирать оптимальные решения для ваших данных.
Реклама. ООО "ОТУС ОНЛАЙН-ОБРАЗОВАНИЕ". ИНН 9705100963.
📅 10 сентября | 20:00 мск | бесплатно
✅ На вебинаре разберёмся, какая СУБД лучше подойдёт для ваших задач:
•Архитектура, производительность и масштабируемость CH и Greenplum
•Различия в хранении и обработке данных
•Как базы показывают себя в OLAP, ETL и ad-hoc аналитике
•Реальные кейсы внедрения и оптимизации
✅ После урока вы сможете:
•Выбирать оптимальное решение под разные сценарии
•Оценивать производительность и удобство работы
•Понимать архитектурные плюсы и минусы каждой СУБД
💥 Участие бесплатное — регистрируйтесь и приходите: [https://otus.pw/SoTpx/?erid=2W5zFJEjnp8]
Бесплатное занятие приурочено к старту курса ClickHouse. После обучения вы научитесь быстро и эффективно настраивать БД, работать с ее продвинутыми функциями, интегрировать с другими системами и выбирать оптимальные решения для ваших данных.
Реклама. ООО "ОТУС ОНЛАЙН-ОБРАЗОВАНИЕ". ИНН 9705100963.
❤4👍1🔥1