Дата в SQL: функции и работа с датами
Дата в SQL требует учитывать тип данных, границы периода и часовой пояс. Разберём работу с датами в SQL: формат даты, извлечение года, текущую дату, разницу дат и группировку по календарным периодам.
Обновлено · Команда SQL Arena
Что потренируете
- Группировать даты по неделям и месяцам через DATE_TRUNC
- Извлекать год, месяц и другие части даты через EXTRACT
- Задавать интервалы времени с помощью INTERVAL
- Фильтровать периоды полуоткрытыми интервалами
- Учитывать часовые пояса при работе с timestamptz
Пример задачи в тренажёре
Так выглядит задача в SQL Arena: условие, редактор, результат и схема базы.
В таблице events хранятся события аккаунтов и время их начала в timestamptz. Нужно взять события с марта по май 2026 года по локальному времени Берлина и вернуть начало месяца в month_start, количество событий в event_count и число аккаунтов с событиями в accounts. Июнь в диапазон не входит. Итог нужно отсортировать по месяцу.
SELECT DATE_TRUNC('month', starts_at AT TIME ZONE 'Europe/Berlin')::date AS month_start, COUNT(*) AS event_count, COUNT(DISTINCT account_id) AS accounts FROM events WHERE starts_at >= TIMESTAMPTZ '2026-03-01 00:00:00 Europe/Berlin' AND starts_at < TIMESTAMPTZ '2026-06-01 00:00:00 Europe/Berlin' GROUP BY 1 ORDER BY 1;
| # | month_start | event_count | accounts |
|---|---|---|---|
| 1 | 2026-03-01 | 2 | 2 |
| 2 | 2026-04-01 | 3 | 3 |
| 3 | 2026-05-01 | 2 | 2 |
В таблице events хранятся события аккаунтов и время их начала в timestamptz. Нужно взять события с марта по май 2026 года по локальному времени Берлина и вернуть начало месяца в month_start, количество событий в event_count и число аккаунтов с событиями в accounts. Июнь в диапазон не входит. Итог нужно отсортировать по месяцу.
SELECT DATE_TRUNC('month', starts_at AT TIME ZONE 'Europe/Berlin')::date AS month_start, COUNT(*) AS event_count, COUNT(DISTINCT account_id) AS accounts FROM events WHERE starts_at >= TIMESTAMPTZ '2026-03-01 00:00:00 Europe/Berlin' AND starts_at < TIMESTAMPTZ '2026-06-01 00:00:00 Europe/Berlin' GROUP BY 1 ORDER BY 1;
Работа с датами в SQL на примере запроса
Фильтр начинается не с DATE_TRUNC, а с точных границ периода. Условие берёт всё от начала 1 марта включительно и до начала 1 июня исключительно. Такая запись называется полуоткрытым интервалом: начало входит, конец нет. Она удобнее конструкции с последним днём месяца, потому что не требует угадывать 23:59:59 и не ломается на значениях с долями секунды. Для следующего месяца достаточно использовать его первую границу.
Поле starts_at имеет тип timestamptz, поэтому перед определением календарного месяца важно решить, в каком часовом поясе считать дату. Выражение starts_at AT TIME ZONE 'Europe/Berlin' переводит момент времени в локальные дату и время Берлина, а DATE_TRUNC('month', ...) отбрасывает день и время до первого числа месяца. Приведение к date оставляет в результате только календарную дату. Благодаря этому событие около полуночи относится к тому месяцу, который видит пользователь в выбранной зоне.
После преобразования GROUP BY собирает события в три месячные группы, COUNT(*) считает строки, а COUNT(DISTINCT account_id) — разные аккаунты. Для задач другого типа пригодится EXTRACT: например, EXTRACT(MONTH FROM starts_at) возвращает номер месяца, а EXTRACT(DOW FROM ...) — день недели. INTERVAL удобен, когда граница строится относительно другой даты: current_date - INTERVAL '30 days' или month_start + INTERVAL '1 month'. Но для фиксированного диапазона явные границы обычно легче проверить.
Набор функций для дат зависит от диалекта SQL. Год из даты можно получать через EXTRACT(YEAR FROM date_value) в системах, которые поддерживают этот синтаксис. Текущая дата часто доступна через CURRENT_DATE. Для разницы дат одни СУБД используют специальные функции вроде DATEDIFF, а другие — вычитание дат или работу с интервалами. Форматирование даты тоже различается по диалектам и не следует путать с её хранением: DATE и TIMESTAMP остаются типами данных независимо от того, как значение показано пользователю. При фильтрации периодов удобно использовать полуоткрытые интервалы: >= начала и < следующей границы.
SELECT DATE_TRUNC('month', starts_at AT TIME ZONE 'Europe/Berlin')::date AS month_start, COUNT(*) AS event_count, COUNT(DISTINCT account_id) AS accounts FROM events WHERE starts_at >= TIMESTAMPTZ '2026-03-01 00:00:00 Europe/Berlin' AND starts_at < TIMESTAMPTZ '2026-06-01 00:00:00 Europe/Berlin' GROUP BY 1 ORDER BY 1;
| month_start | event_count | accounts |
|---|---|---|
| 2026-03-01 | 2 | 2 |
| 2026-04-01 | 3 | 3 |
| 2026-05-01 | 2 | 2 |
Ошибки при работе с датой и временем в SQL
BETWEEN для диапазона месяцев
Запись BETWEEN '2026-03-01' AND '2026-05-31' выглядит естественно, но верхняя граница превращается в начало 31 мая и может исключить события позже полуночи. Надёжнее писать start >= нижняя_граница AND start < следующая_граница. Этот шаблон одинаково работает для дней, месяцев и лет.
DATE_TRUNC до перевода часового пояса
Если сначала определить месяц в одной временной зоне, а потом показать время в другой, события около полуночи могут оказаться в неправильной группе. Для timestamptz сначала определяют бизнес-зону, затем получают локальную дату или начало периода.
EXTRACT(MONTH) без года
Группировка только по номеру месяца объединит январь 2025 и январь 2026. Если данные охватывают несколько лет, группируйте по DATE_TRUNC('month', date_column) либо добавляйте в ключ и год через EXTRACT(YEAR FROM ...).
Задачи по теме в тренажёре
Трек «SQL с нуля → Аналитик»После такого примера полезно решить несколько задач с разными видами временных условий. В бесплатных Easy-задачах можно потренироваться находить пациентов, родившихся в мае, удалять устаревшие строки аудит-лога, выбирать последний вход пользователя за конкретный год и находить сессии, которые остаются открытыми дольше суток. Дальше встречаются задачи на временные окна и расходы за месяц или год. Если базовые фильтры уже получаются уверенно, следующий логичный маршрут — трек «SQL с нуля → Аналитик»: там работа с датами встречается вместе с другими приёмами, которые нужны в аналитических запросах.
- EasyбесплатноПациенты, родившиеся в маеpostgresqlmysqlclickhouse
- EasyбесплатноОчистить устаревшие записи аудит-логаpostgresqlmysql
- EasyбесплатноПоследний вход пользователя в 2020 годуpostgresqlmysqlclickhouse
- EasyбесплатноЗависшие сессии дольше сутокpostgresqlmysqlclickhouse
- MediumPremiumТур-выезды в заданном временном окнеpostgresqlmysqlclickhouse
- MediumPremiumРасходы членов клуба в 2005 годуpostgresqlmysqlclickhouse
- MediumPremiumРасходы в июне 2005 годаpostgresqlmysqlclickhouse
- MediumPremiumРасходы за 2005 год по жанрамpostgresqlmysqlclickhouse
Справочник по теме
Подробные статьи с синтаксисом и примерами в справочнике SQL Arena.
Частые вопросы
Как хранится дата в SQL?
Для календарной даты обычно используют тип DATE, а для даты со временем — типы семейства TIMESTAMP. Конкретные типы и поддержка часовых поясов зависят от СУБД.
Как изменить формат даты в SQL?
Способ форматирования зависит от диалекта SQL и обычно выполняется специальной функцией преобразования даты в строку. Форматирование меняет представление значения, а не сам тип хранимой даты.
Как получить год из даты в SQL?
Во многих SQL-диалектах год можно получить выражением EXTRACT(YEAR FROM date_value). В некоторых СУБД также доступны отдельные функции для извлечения года.
Как получить текущую дату в SQL?
Стандартное выражение CURRENT_DATE возвращает текущую календарную дату в поддерживающих его СУБД. Для текущих даты и времени обычно применяются отдельные timestamp-функции.
Как посчитать разницу дат в SQL и использовать DATEDIFF?
Синтаксис зависит от СУБД. В одних диалектах есть функция DATEDIFF, в других даты вычитают напрямую или используют функции для вычисления интервала между значениями.
Практика функций даты в SQL
В SQL Arena можно продолжить практику на задачах с отдельными таблицами и автоматической проверкой на скрытых данных. Бесплатно доступны 292 задачи на PostgreSQL, MySQL, ClickHouse и BigQuery, а для следующего шага можно пройти трек «SQL с нуля → Аналитик».
Решать задачи по датам