При прямом UPDATE данные теряют историю: невозможно корректно восстановить состояние сущности на дату или отследить момент изменения.
Сегодня в гайде:
• Как моделировать версионные данные через valid_from / valid_to;
• Как корректно закрывать предыдущую версию и создавать новую;
• Как получать актуальное состояние и исторические срезы без дополнительной логики.
SCD Type 2 хранит каждое изменение как отдельную версию строки с фиксированным интервалом актуальности.
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
❤12👍10🤝8🔥3
Например, Raft выбирает лидера через голосование, Paxos использует кворум, а Bully Algorithm назначает лидером узел с максимальным ID.
На картинке — 5 базовых алгоритмов leader election, которые используются в распределённых БД и системах координации.
Сохрани, чтобы не забыть!
Please open Telegram to view this post
VIEW IN TELEGRAM
👍13❤8🔥8
NULL и сравнения — почему = и <> с NULL не работают!
Таблица:
Нужно выбрать неудалённых пользователей.
Типичный, но неправильный запрос:
Запрос выполнится, но вернёт 0 строк.
Почему так — SQL выражение:
никогда не бывает TRUE.
Любое сравнение с
То же самое с отрицанием:
Результат — тоже пусто.
Правильный способ —
И обратное условие:
Частая скрытая ошибка в составных условиях:
Если
Корректная проверка «не удалён»:
Важно помнить про
Здесь логика работает ожидаемо, потому что
🔥 Практические правила: = и <> с
➡️ SQL Ready | #практика
NULL в SQL — не значение, а его отсутствие. Из-за этого любые обычные сравнения с NULL ведут себя не так, как ожидают, и часто ломают фильтрацию.Таблица:
users(id, email, deleted_at)
Нужно выбрать неудалённых пользователей.
Типичный, но неправильный запрос:
SELECT *
FROM users
WHERE deleted_at = NULL;
Запрос выполнится, но вернёт 0 строк.
Почему так — SQL выражение:
deleted_at = NULL
никогда не бывает TRUE.
Любое сравнение с
NULL (=, <>, <, >) возвращает UNKNOWN, а WHERE пропускает только TRUE.То же самое с отрицанием:
WHERE deleted_at <> NULL;
Результат — тоже пусто.
Правильный способ —
IS NULL:SELECT *
FROM users
WHERE deleted_at IS NULL;
И обратное условие:
SELECT *
FROM users
WHERE deleted_at IS NOT NULL;
IS NULL и IS NOT NULL — всегда возвращают TRUE или FALSE, никогда UNKNOWN.Частая скрытая ошибка в составных условиях:
WHERE status = 'active'
AND deleted_at <> '2026-01-01'
Если
deleted_at равен NULL, всё выражение становится UNKNOWN, и строка отбрасывается, даже если status = 'active'.Корректная проверка «не удалён»:
WHERE status = 'active'
AND deleted_at IS NULL
Важно помнить про
OR:WHERE role = 'admin'
OR deleted_at IS NULL
Здесь логика работает ожидаемо, потому что
IS NULL не возвращает UNKNOWN, а значит выражение может стать TRUE.NULL не работают в WHERE; любой UNKNOWN в WHERE — строка отбрасывается. Если строка пропала из результата — первым делом проверяй условия с NULLPlease open Telegram to view this post
VIEW IN TELEGRAM
❤15👍9🔥7🤝4
Знали, что в SQL можно получить несколько уровней агрегации за один проход по данным?
Частая задача - посчитать метрики сразу на нескольких уровнях: по стране и статусу, только по стране и общий итог. Обычно для этого пишут несколько
Каждая скобка - отдельный уровень агрегации. PostgreSQL делает это за один проход по данным, без лишних сканов.
Строка с пустыми скобками
🔥 Этот приём особенно полезен в отчётах и аналитике, где нужны totals, subtotals и детализация одновременно.
➡️ SQL Ready | #совет
Частая задача - посчитать метрики сразу на нескольких уровнях: по стране и статусу, только по стране и общий итог. Обычно для этого пишут несколько
SELECT и склеивают их UNION ALL:SELECT country, status, COUNT(*) FROM orders GROUP BY country, status
UNION ALL
SELECT country, NULL, COUNT(*) FROM orders GROUP BY country
UNION ALL
SELECT NULL, NULL, COUNT(*) FROM orders;
GROUPING SETS позволяет описать все нужные уровни агрегации в одном GROUP BY: GROUP BY GROUPING SETS ((country, status), (country), ())
Каждая скобка - отдельный уровень агрегации. PostgreSQL делает это за один проход по данным, без лишних сканов.
Строка с пустыми скобками
() - это глобальный итог. Колонки, которые не участвуют в текущем уровне, приходят как NULL:(country = NULL, status = NULL)
Please open Telegram to view this post
VIEW IN TELEGRAM
❤20👍14🔥10🤝2
🔥13👍9❤6🤝4👎1
NULL и сравнения — почему = и <> с NULL не работают!
Таблица:
Нужно выбрать неудалённых пользователей.
Типичный, но неправильный запрос:
Запрос выполнится, но вернёт 0 строк.
Почему так — SQL выражение:
никогда не бывает TRUE.
Любое сравнение с
То же самое с отрицанием:
Результат — тоже пусто.
Правильный способ —
И обратное условие:
Частая скрытая ошибка в составных условиях:
Если
Корректная проверка «не удалён»:
Важно помнить про
Здесь логика работает ожидаемо, потому что
🔥 Практические правила: = и <> с
➡️ SQL Ready | #практика
NULL в SQL — не значение, а его отсутствие. Из-за этого любые обычные сравнения с NULL ведут себя не так, как ожидают, и часто ломают фильтрацию.Таблица:
users(id, email, deleted_at)
Нужно выбрать неудалённых пользователей.
Типичный, но неправильный запрос:
SELECT *
FROM users
WHERE deleted_at = NULL;
Запрос выполнится, но вернёт 0 строк.
Почему так — SQL выражение:
deleted_at = NULL
никогда не бывает TRUE.
Любое сравнение с
NULL (=, <>, <, >) возвращает UNKNOWN, а WHERE пропускает только TRUE.То же самое с отрицанием:
WHERE deleted_at <> NULL;
Результат — тоже пусто.
Правильный способ —
IS NULL:SELECT *
FROM users
WHERE deleted_at IS NULL;
И обратное условие:
SELECT *
FROM users
WHERE deleted_at IS NOT NULL;
IS NULL и IS NOT NULL — всегда возвращают TRUE или FALSE, никогда UNKNOWN.Частая скрытая ошибка в составных условиях:
WHERE status = 'active'
AND deleted_at <> '2026-01-01'
Если
deleted_at равен NULL, всё выражение становится UNKNOWN, и строка отбрасывается, даже если status = 'active'.Корректная проверка «не удалён»:
WHERE status = 'active'
AND deleted_at IS NULL
Важно помнить про
OR:WHERE role = 'admin'
OR deleted_at IS NULL
Здесь логика работает ожидаемо, потому что
IS NULL не возвращает UNKNOWN, а значит выражение может стать TRUE.NULL не работают в WHERE; любой UNKNOWN в WHERE — строка отбрасывается. Если строка пропала из результата — первым делом проверяй условия с NULLPlease open Telegram to view this post
VIEW IN TELEGRAM
🔥17❤12👍11🤝1
Например, Relational / SQL базы подходят для строгих транзакций и сложных связей, Time-series — для метрик, логов и мониторинга, а NoSQL — когда важны масштабирование, гибкость схемы и высокая нагрузка.
На картинке — 3 типа баз данных с примерами и их основными особенностями.
Сохрани, чтобы не забыть!
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥15👍9🤝9❤1👎1
Шпаргалка по строковым функциям MySQL для повседневной работы. Разбор приёмов вычисления длины строк, извлечения подстрок, поиска значений, управления регистром и правилами сравнения. Полезно для валидации данных, сортировок, фильтрации, миграций и тонкой настройки запросов без изменения схемы БД.Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11👍10🤝7❤2
This media is not supported in your browser
VIEW IN TELEGRAM
Небольшой, но ёмкий квиз, который проверяет логику работы SQL-запросов. Вопросы заставляют подумать: как реально отработает запрос, где скрыта ошибка и почему результат не такой, как ожидаешь. Отлично подходит для самопроверки и подготовки к собеседованиям.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍16❤10🔥7🤝3
WHERE vs HAVING — фильтрация строк и фильтрация групп!
В SQL иногда путают
Представим таблицу заказов:
Нужно выбрать заказы дороже 100.
Корректный и очевидный вариант:
Теперь другая задача: найти клиентов, у которых сумма всех заказов больше 1000.
Интуитивная, но неверная попытка:
Этот запрос некорректен: агрегатные функции не могут использоваться в
Правильный вариант —
Важно понимать разницу по этапам:
Частая оптимизационная ошибка — переносить условия из
Такой запрос либо не выполнится, либо будет логически неверным:
Корректный и оптимальный вариант — комбинировать:
Здесь:
🔥 Используй
➡️ SQL Ready | #практика
В SQL иногда путают
WHERE и HAVING, потому что внешне они решают похожую задачу — отфильтровать результат. На практике это два разных этапа выполнения запроса с разной семантикой.Представим таблицу заказов:
orders(id, customer_id, amount)
Нужно выбрать заказы дороже 100.
Корректный и очевидный вариант:
SELECT *
FROM orders
WHERE amount > 100;
WHERE применяется до агрегации и фильтрует отдельные строки исходной таблицы.Теперь другая задача: найти клиентов, у которых сумма всех заказов больше 1000.
Интуитивная, но неверная попытка:
SELECT customer_id, SUM(amount) AS total_amount
FROM orders
WHERE SUM(amount) > 1000
GROUP BY customer_id;
Этот запрос некорректен: агрегатные функции не могут использоваться в
WHERE, потому что на момент применения WHERE агрегатов ещё не существует.Правильный вариант —
HAVING:SELECT customer_id, SUM(amount) AS total_amount
FROM orders
GROUP BY customer_id
HAVING SUM(amount) > 1000;
HAVING применяется после GROUP BY и фильтрует уже сформированные группы.Важно понимать разницу по этапам:
WHERE — фильтрует строки до группировки; GROUP BY — формирует группы; HAVING — фильтрует группы; SELECT — формирует итоговый результат.Частая оптимизационная ошибка — переносить условия из
WHERE в HAVING без необходимости:SELECT customer_id, SUM(amount)
FROM orders
GROUP BY customer_id
HAVING amount > 100;
Такой запрос либо не выполнится, либо будет логически неверным:
amount — это поле строки, а HAVING работает с группой.Корректный и оптимальный вариант — комбинировать:
SELECT customer_id, SUM(amount) AS total_amount
FROM orders
WHERE amount > 100
GROUP BY customer_id
HAVING SUM(amount) > 1000;
Здесь:
WHERE отсекает мелкие заказы до агрегации (меньше данных); HAVING проверяет условие на уровне группы.WHERE для фильтрации строк и HAVING — только для условий на агрегаты и группы. Это влияет не только на корректность, но и на производительность запросаPlease open Telegram to view this post
VIEW IN TELEGRAM
🔥20👍14❤12🤝1
LATERAL — коррелированные подзапросы!
Часто бывает нужно для каждой строки основной таблицы взять одну лучшую связанную строку - последнюю, первую, максимальную, минимальную, валидную.
Обычно это делают через оконки, CTE или GROUP BY + JOIN, что сложно читать и часто ломается при расширении логики:
Выполняет подзапрос для каждой строки слева, подставляя её значения.
Гарантирует, что ты берёшь именно нужную запись:
Потому что связь уже внутри подзапроса, JOIN тут формальность.
🔥 Это отличный инструмент, когда нужна одна зависимая строка на родителя
➡️ SQL Ready | #совет
Часто бывает нужно для каждой строки основной таблицы взять одну лучшую связанную строку - последнюю, первую, максимальную, минимальную, валидную.
Обычно это делают через оконки, CTE или GROUP BY + JOIN, что сложно читать и часто ломается при расширении логики:
LEFT JOIN LATERAL (...)
Выполняет подзапрос для каждой строки слева, подставляя её значения.
ORDER BY created_at DESC
LIMIT 1
Гарантирует, что ты берёшь именно нужную запись:
ON true
Потому что связь уже внутри подзапроса, JOIN тут формальность.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍15🔥8🤝8
This media is not supported in your browser
VIEW IN TELEGRAM
Если хочешь не просто учить синтаксис, а писать реальные запросы и практиковаться. Это отличный способ закрепить знания по выборкам, фильтрации, сортировке, условиям и множеству других тем, особенно если готовишься к собесам.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤14🔥9👍8
Частая проблема в аналитике и BI - таблицы с десятками колонок?
Пока колонок три это может быть терпимо. Когда их становится двадцать и больше, запросы, джоины и графики начинают разваливаться.
На выходе получается универсальная структура
Полезно, когда нужно сравнивать метрики между собой
или передавать данные в BI без кастомной логики:
🔥 Один запрос - десятки метрик и минимум изменений при росте отчёта (новые метрики просто добавляются в список UNPIVOT).
➡️ SQL Ready | #совет
Пока колонок три это может быть терпимо. Когда их становится двадцать и больше, запросы, джоины и графики начинают разваливаться.
UNPIVOT позволяет превратить колонки в строки на уровне SQL, без переписывания данных и логики приложения (синтаксис зависит от СУБД):UNPIVOT (value FOR metric IN (views, clicks))
На выходе получается универсальная структура
metric / value, которая хорошо ложится в агрегации, фильтры и визуализации BI.Полезно, когда нужно сравнивать метрики между собой
или передавать данные в BI без кастомной логики:
WHERE metric IN ('views', 'orders')Please open Telegram to view this post
VIEW IN TELEGRAM
👍15🔥8❤6🤝1