INTERVAL в SQL: шпаргалка
Карьерник — квиз-тренажёр в Telegram с 1500+ вопросами для собесов аналитика. SQL, Python, A/B, метрики. Бесплатно.
Что такое INTERVAL
INTERVAL — тип данных в SQL для хранения временных промежутков (дни, месяцы, часы). Позволяет делать арифметику с датами.
INTERVAL '1 day'
INTERVAL '7 days'
INTERVAL '2 hours'
INTERVAL '3 months'
INTERVAL '1 year 6 months'Синтаксис по СУБД
PostgreSQL
CURRENT_DATE + INTERVAL '7 days'
NOW() - INTERVAL '1 hour'MySQL
DATE_ADD(CURDATE(), INTERVAL 7 DAY)
CURDATE() - INTERVAL 7 DAYClickHouse
today() + INTERVAL 7 DAY
now() - INTERVAL 1 HOURBigQuery / Snowflake
-- BigQuery
DATE_ADD(CURRENT_DATE(), INTERVAL 7 DAY)
-- Snowflake
DATEADD(DAY, 7, CURRENT_DATE())1. Прибавить/отнять к дате
-- Postgres
SELECT NOW() + INTERVAL '30 days';
SELECT CURRENT_DATE - INTERVAL '1 month';
SELECT '2026-01-01'::DATE + INTERVAL '1 year';
-- MySQL
SELECT CURDATE() + INTERVAL 7 DAY;
SELECT NOW() - INTERVAL 1 HOUR;2. Заказы за последние 7 дней
-- Postgres
SELECT * FROM orders
WHERE created_at >= NOW() - INTERVAL '7 days';
-- MySQL
SELECT * FROM orders
WHERE created_at >= NOW() - INTERVAL 7 DAY;3. Заказы в текущем месяце
SELECT * FROM orders
WHERE created_at >= DATE_TRUNC('month', CURRENT_DATE)
AND created_at < DATE_TRUNC('month', CURRENT_DATE) + INTERVAL '1 month';4. Предыдущий месяц
-- Postgres
SELECT * FROM orders
WHERE created_at >= DATE_TRUNC('month', CURRENT_DATE) - INTERVAL '1 month'
AND created_at < DATE_TRUNC('month', CURRENT_DATE);5. Пользователи, зарегистрировавшиеся сегодня
SELECT * FROM users
WHERE created_at >= CURRENT_DATE
AND created_at < CURRENT_DATE + INTERVAL '1 day';6. Разность между датами
Postgres
-- разница в INTERVAL
SELECT MAX(created_at) - MIN(created_at) AS span
FROM orders WHERE user_id = 1;
-- разница в днях (int)
SELECT (MAX(created_at) - MIN(created_at))::INT AS days
FROM orders;
-- альтернатива через AGE
SELECT EXTRACT(DAY FROM AGE(max_date, min_date)) AS days;MySQL
SELECT DATEDIFF(max_date, min_date) AS days;
SELECT TIMESTAMPDIFF(HOUR, start_at, end_at) AS hours;
SELECT TIMESTAMPDIFF(MONTH, birth_date, CURDATE()) AS months;ClickHouse
SELECT dateDiff('day', start_at, end_at) AS days;
SELECT dateDiff('hour', start_at, end_at) AS hours;7. Комбинированные интервалы
-- Postgres
SELECT NOW() + INTERVAL '1 year 6 months 7 days';
-- MySQL (только один компонент за раз)
SELECT DATE_ADD(DATE_ADD(NOW(), INTERVAL 1 YEAR), INTERVAL 6 MONTH);8. Время между заказами пользователя
-- Postgres
SELECT
user_id,
order_id,
created_at - LAG(created_at) OVER (
PARTITION BY user_id ORDER BY created_at
) AS time_since_prev_order
FROM orders;9. Пользователи, неактивные 30+ дней
SELECT user_id, MAX(event_at) AS last_event
FROM events
GROUP BY user_id
HAVING MAX(event_at) < NOW() - INTERVAL '30 days';10. Календарь дней
-- Postgres — generate_series
SELECT day::DATE
FROM generate_series(
DATE '2026-01-01',
DATE '2026-12-31',
INTERVAL '1 day'
) AS day;11. Группировка по интервалам
По неделе
SELECT DATE_TRUNC('week', created_at) AS week, COUNT(*)
FROM orders GROUP BY 1;По 15-минутным интервалам
-- Postgres
SELECT
DATE_TRUNC('hour', created_at)
+ FLOOR(EXTRACT(MINUTE FROM created_at) / 15) * INTERVAL '15 minutes' AS bucket,
COUNT(*)
FROM orders
GROUP BY 1;12. Арифметика с интервалами
-- умножение
SELECT INTERVAL '1 day' * 7; -- 7 дней
SELECT '2026-01-01'::DATE + (n * INTERVAL '1 month')
FROM generate_series(0, 11) n; -- месяцы 2026
-- сумма
SELECT INTERVAL '1 hour' + INTERVAL '30 minutes'; -- 01:30:00Что такое таймзоны
Дата без таймзоны (TIMESTAMP) и с таймзоной (TIMESTAMPTZ) ведут себя по-разному:
-- Postgres
SELECT NOW(); -- 2026-04-21 10:30:00+03
SELECT NOW() AT TIME ZONE 'UTC'; -- 07:30:00 (без зоны)
SELECT '2026-04-21 10:00'::TIMESTAMPTZ AT TIME ZONE 'Europe/Moscow';Если сервер в UTC, а анализ в МСК — обязательно конвертируйте перед группировкой по дням.
Частые ошибки
Ошибка 1. BETWEEN исключает часть времени
-- теряем события 2026-01-31 23:00+
WHERE created_at BETWEEN '2026-01-01' AND '2026-01-31'
-- правильно
WHERE created_at >= '2026-01-01'
AND created_at < '2026-02-01'Ошибка 2. INTERVAL '1 month' ≠ 30 days
Месяц — переменной длины. Добавление INTERVAL '1 month' к 31 января даст 28 февраля (или 29).
Ошибка 3. Смешивание TIMESTAMP и TIMESTAMPTZ
Часто приводит к сюрпризам с таймзонами. Выбирайте один тип и придерживайтесь.
Ошибка 4. NULL в арифметике
-- если last_login IS NULL, вернётся NULL
last_login < NOW() - INTERVAL '30 days'
-- учитывайте NULL отдельно
last_login IS NULL OR last_login < NOW() - INTERVAL '30 days'Ошибка 5. «Неделя» начинается по-разному
Postgres ISO: понедельник. MySQL: воскресенье / настраивается. Уточняйте.
Связанные темы
FAQ
Можно ли INTERVAL в арифметике?
Да: INTERVAL × number, INTERVAL + INTERVAL. Умножение интервалов между собой — нет.
Как получить разницу в минутах?
Postgres: EXTRACT(EPOCH FROM (a - b)) / 60. MySQL: TIMESTAMPDIFF(MINUTE, b, a). ClickHouse: dateDiff('minute', b, a).
INTERVAL и DATE_ADD — что использовать?
В Postgres — INTERVAL (proпер тип). В MySQL — оба работают, INTERVAL обычно короче.
Как посчитать возраст в годах?
Postgres: EXTRACT(YEAR FROM AGE(birth_date)). MySQL: TIMESTAMPDIFF(YEAR, birth_date, CURDATE()).
Тренируйте SQL — откройте тренажёр с 1500+ вопросами для собесов.