EXTRACT — это «вытащи отдельный компонент из даты или времени». Год, месяц, день, час, день недели — что угодно из даты можно достать отдельным числом.
Это стандартный SQL-приём для группировок и фильтров: «сколько регистраций по месяцам», «выручка за каждый понедельник», «сколько секунд от epoch».
Дата — это сложный тип. В одной timestamp-строке зашит и год, и месяц, и день, и время. Часто нужна не вся дата целиком, а её кусок:
- Группировка по году: «регистрации по годам».
- Фильтр по дню недели: «события только в будни».
- Расчёт через EPOCH: «сколько секунд прошло между событиями».
EXTRACT отвечает именно на эти задачи.
Базовый синтаксис
SELECT EXTRACT(YEAR FROM TIMESTAMP '2026-03-15 10:30:00');
SELECT EXTRACT(MONTH FROM DATE '2026-03-15');
SELECT EXTRACT(DAY FROM DATE '2026-03-15');
SELECT EXTRACT(HOUR FROM TIMESTAMP '2026-03-15 10:30:00');
Структура: EXTRACT(<поле> FROM <выражение>). Возвращает число.
Самые частые поля
| Поле |
Что возвращает |
YEAR |
Год |
MONTH |
Месяц (1-12) |
DAY |
День месяца (1-31) |
HOUR |
Час (0-23) |
MINUTE |
Минута (0-59) |
SECOND |
Секунда (0-60, с микросекундами как дробь) |
DOW |
День недели (0=воскресенье, 6=суббота) |
ISODOW |
День недели по ISO (1=пн, 7=вс) |
DOY |
День года (1-366) |
WEEK |
Номер ISO-недели (1-53) |
QUARTER |
Квартал (1-4) |
EPOCH |
Секунд с 1970-01-01 UTC |
Группировка по периодам
SELECT
EXTRACT(YEAR FROM created_at) AS year,
EXTRACT(MONTH FROM created_at) AS month,
COUNT(*) AS regs
FROM users
WHERE created_at >= '2026-01-01' AND created_at < '2027-01-01'
GROUP BY 1, 2
ORDER BY 1, 2;
Альтернатива через DATE_TRUNC — короче и обычно удобнее:
SELECT
DATE_TRUNC('month', created_at) AS month,
COUNT(*) AS regs
FROM users
WHERE created_at >= '2026-01-01' AND created_at < '2027-01-01'
GROUP BY 1
ORDER BY 1;
DATE_TRUNC('month', date) — обрезает дату до начала месяца (2026-03-15 → 2026-03-01). Возвращает timestamp, удобно для UI.
EXTRACT — возвращает число. Удобно когда нужны отдельно year и month как разные колонки.
Фильтр по компоненту даты
SELECT * FROM events WHERE EXTRACT(MONTH FROM occurred_at) = 3;
SELECT * FROM events WHERE EXTRACT(ISODOW FROM occurred_at) = 1;
SELECT * FROM events
WHERE EXTRACT(ISODOW FROM occurred_at) BETWEEN 1 AND 5
AND EXTRACT(HOUR FROM occurred_at) BETWEEN 9 AND 17;
Внимание: EXTRACT в WHERE мешает обычному индексу на occurred_at. На больших таблицах — либо functional index CREATE INDEX ON events (EXTRACT(MONTH FROM occurred_at)), либо переписать через диапазон:
SELECT * FROM events
WHERE occurred_at >= '2026-03-01' AND occurred_at < '2026-04-01';
Диапазонная форма всегда быстрее, если возможна.
EPOCH и интервалы
EPOCH особенно полезен для измерения интервалов в секундах:
SELECT EXTRACT(EPOCH FROM ('2026-03-15 10:30:00' - '2026-03-15 09:00:00'))::INTEGER;
SELECT
name,
EXTRACT(EPOCH FROM (NOW() - created_at)) / 86400 AS days_since_signup
FROM users;
EXTRACT(EPOCH FROM x) для timestamp возвращает секунды с Unix epoch:
SELECT EXTRACT(EPOCH FROM TIMESTAMP '2026-03-15 12:00:00');
Пример с таблицей
orders:
| id |
created_at |
amount |
| 1 |
2026-01-15 10:00:00 |
100 |
| 2 |
2026-02-03 14:00:00 |
250 |
| 3 |
2026-02-20 11:30:00 |
80 |
| 4 |
2026-03-05 09:00:00 |
500 |
Сводка по месяцам:
SELECT
EXTRACT(MONTH FROM created_at) AS month,
COUNT(*) AS orders_count,
SUM(amount) AS total
FROM orders
GROUP BY 1
ORDER BY 1;
| month |
orders_count |
total |
| 1 |
1 |
100 |
| 2 |
2 |
330 |
| 3 |
1 |
500 |
Отличия диалектов
MySQL не поддерживает EXTRACT(EPOCH FROM …). Аналог — UNIX_TIMESTAMP(date):
SELECT UNIX_TIMESTAMP('2026-03-15 12:00:00');
SELECT YEAR('2026-03-15');
SELECT MONTH('2026-03-15');
SELECT DAYOFWEEK('2026-03-15');
В MySQL также есть YEAR(), MONTH(), DAY(), HOUR() — короткие функции для отдельных компонентов.
SQLite не имеет EXTRACT. Используют STRFTIME:
SELECT STRFTIME('%Y', '2026-03-15');
SELECT STRFTIME('%m', '2026-03-15');
SELECT STRFTIME('%w', '2026-03-15');
SQL Server — DATEPART:
SELECT DATEPART(year, '2026-03-15');
SELECT DATEPART(month, '2026-03-15');
Частые ошибки новичков
1. EXTRACT в WHERE без functional index. Делает full scan. Перепиши через диапазон если возможно (см. выше).
2. DOW vs ISODOW. EXTRACT(DOW) — 0=воскресенье. EXTRACT(ISODOW) — 1=понедельник, 7=воскресенье. Если хочешь «нормальный» календарный понедельник — используй ISODOW.
3. Часовые пояса. EXTRACT(HOUR FROM TIMESTAMPTZ) использует timezone сессии. Если в БД timestamp UTC, а пользователь в Москве — EXTRACT(HOUR ...) без AT TIME ZONE даст UTC-часы, не московские.
EXTRACT(HOUR FROM tstz AT TIME ZONE 'Europe/Moscow')
4. EPOCH возвращает дробное. Для timestamp с микросекундами — 1773993600.123456. Если нужен integer Unix timestamp — каст: EXTRACT(EPOCH FROM …)::BIGINT.
5. WEEK у разных БД отличается. PostgreSQL EXTRACT(WEEK) — ISO-неделя. MySQL WEEK(date) — несколько mode'ов в зависимости от настроек. При портировании проверяй.
6. Quarter/Year в группировках. GROUP BY EXTRACT(QUARTER FROM x) без EXTRACT(YEAR FROM x) смешает Q1 разных лет в одну группу. Всегда добавляй год для корректности.
Мини-резюме
EXTRACT(<поле> FROM <дата>) — достать год, месяц, день, час и т.д. как число.
- Самые частые поля:
YEAR, MONTH, DAY, HOUR, ISODOW, EPOCH.
ISODOW (1=пн, 7=вс) — обычно то, что хочешь, а не DOW (0=вс).
EXTRACT(EPOCH FROM ...) — секунды от Unix-epoch или из интервала. Для целого числа — ::BIGINT.
- Для группировок по периоду часто удобнее
DATE_TRUNC('month', x) — возвращает timestamp начала периода.
- В
WHERE — диапазонная форма (>= '2026-03-01' AND < '2026-04-01') быстрее, чем EXTRACT, потому что использует индекс.
- В MySQL для EPOCH —
UNIX_TIMESTAMP. В SQLite — STRFTIME. В SQL Server — DATEPART.
EXTRACT— это «вытащи отдельный компонент из даты или времени». Год, месяц, день, час, день недели — что угодно из даты можно достать отдельным числом.Это стандартный SQL-приём для группировок и фильтров: «сколько регистраций по месяцам», «выручка за каждый понедельник», «сколько секунд от epoch».
Зачем нужен EXTRACT
Дата — это сложный тип. В одной timestamp-строке зашит и год, и месяц, и день, и время. Часто нужна не вся дата целиком, а её кусок:
EXTRACTотвечает именно на эти задачи.Базовый синтаксис
SELECT EXTRACT(YEAR FROM TIMESTAMP '2026-03-15 10:30:00'); -- 2026 SELECT EXTRACT(MONTH FROM DATE '2026-03-15'); -- 3 SELECT EXTRACT(DAY FROM DATE '2026-03-15'); -- 15 SELECT EXTRACT(HOUR FROM TIMESTAMP '2026-03-15 10:30:00'); -- 10Структура:
EXTRACT(<поле> FROM <выражение>). Возвращает число.Самые частые поля
YEARMONTHDAYHOURMINUTESECONDDOWISODOWDOYWEEKQUARTEREPOCHГруппировка по периодам
-- Регистрации по месяцам 2026 года SELECT EXTRACT(YEAR FROM created_at) AS year, EXTRACT(MONTH FROM created_at) AS month, COUNT(*) AS regs FROM users WHERE created_at >= '2026-01-01' AND created_at < '2027-01-01' GROUP BY 1, 2 ORDER BY 1, 2;Альтернатива через
DATE_TRUNC— короче и обычно удобнее:SELECT DATE_TRUNC('month', created_at) AS month, COUNT(*) AS regs FROM users WHERE created_at >= '2026-01-01' AND created_at < '2027-01-01' GROUP BY 1 ORDER BY 1;DATE_TRUNC('month', date)— обрезает дату до начала месяца (2026-03-15→2026-03-01). Возвращает timestamp, удобно для UI.EXTRACT— возвращает число. Удобно когда нужны отдельноyearиmonthкак разные колонки.Фильтр по компоненту даты
-- Все события, случившиеся в марте (любого года) SELECT * FROM events WHERE EXTRACT(MONTH FROM occurred_at) = 3; -- Только понедельники SELECT * FROM events WHERE EXTRACT(ISODOW FROM occurred_at) = 1; -- Рабочие часы — пн-пт, 9-18 SELECT * FROM events WHERE EXTRACT(ISODOW FROM occurred_at) BETWEEN 1 AND 5 AND EXTRACT(HOUR FROM occurred_at) BETWEEN 9 AND 17;Внимание:
EXTRACTвWHEREмешает обычному индексу наoccurred_at. На больших таблицах — либо functional indexCREATE INDEX ON events (EXTRACT(MONTH FROM occurred_at)), либо переписать через диапазон:-- Это использует обычный индекс SELECT * FROM events WHERE occurred_at >= '2026-03-01' AND occurred_at < '2026-04-01';Диапазонная форма всегда быстрее, если возможна.
EPOCH и интервалы
EPOCHособенно полезен для измерения интервалов в секундах:-- Сколько секунд между двумя timestamp'ами SELECT EXTRACT(EPOCH FROM ('2026-03-15 10:30:00' - '2026-03-15 09:00:00'))::INTEGER; -- → 5400 (1.5 часа) -- Возраст пользователей в днях от регистрации SELECT name, EXTRACT(EPOCH FROM (NOW() - created_at)) / 86400 AS days_since_signup FROM users;EXTRACT(EPOCH FROM x)для timestamp возвращает секунды с Unix epoch:SELECT EXTRACT(EPOCH FROM TIMESTAMP '2026-03-15 12:00:00'); -- → 1773993600Пример с таблицей
orders:Сводка по месяцам:
SELECT EXTRACT(MONTH FROM created_at) AS month, COUNT(*) AS orders_count, SUM(amount) AS total FROM orders GROUP BY 1 ORDER BY 1;Отличия диалектов
MySQL не поддерживает
EXTRACT(EPOCH FROM …). Аналог —UNIX_TIMESTAMP(date):-- MySQL SELECT UNIX_TIMESTAMP('2026-03-15 12:00:00'); -- 1773993600 SELECT YEAR('2026-03-15'); -- 2026 SELECT MONTH('2026-03-15'); -- 3 SELECT DAYOFWEEK('2026-03-15'); -- 1=воскресенье в MySQLВ MySQL также есть
YEAR(),MONTH(),DAY(),HOUR()— короткие функции для отдельных компонентов.SQLite не имеет
EXTRACT. ИспользуютSTRFTIME:SELECT STRFTIME('%Y', '2026-03-15'); -- '2026' SELECT STRFTIME('%m', '2026-03-15'); -- '03' SELECT STRFTIME('%w', '2026-03-15'); -- день недели, 0=воскресеньеSQL Server —
DATEPART:SELECT DATEPART(year, '2026-03-15'); -- 2026 SELECT DATEPART(month, '2026-03-15'); -- 3Частые ошибки новичков
1. EXTRACT в WHERE без functional index. Делает full scan. Перепиши через диапазон если возможно (см. выше).
2. DOW vs ISODOW.
EXTRACT(DOW)— 0=воскресенье.EXTRACT(ISODOW)— 1=понедельник, 7=воскресенье. Если хочешь «нормальный» календарный понедельник — используйISODOW.3. Часовые пояса.
EXTRACT(HOUR FROM TIMESTAMPTZ)использует timezone сессии. Если в БД timestamp UTC, а пользователь в Москве —EXTRACT(HOUR ...)безAT TIME ZONEдаст UTC-часы, не московские.-- Час в московском времени EXTRACT(HOUR FROM tstz AT TIME ZONE 'Europe/Moscow')4. EPOCH возвращает дробное. Для timestamp с микросекундами —
1773993600.123456. Если нужен integer Unix timestamp — каст:EXTRACT(EPOCH FROM …)::BIGINT.5. WEEK у разных БД отличается. PostgreSQL
EXTRACT(WEEK)— ISO-неделя. MySQLWEEK(date)— несколько mode'ов в зависимости от настроек. При портировании проверяй.6. Quarter/Year в группировках.
GROUP BY EXTRACT(QUARTER FROM x)безEXTRACT(YEAR FROM x)смешает Q1 разных лет в одну группу. Всегда добавляй год для корректности.Мини-резюме
EXTRACT(<поле> FROM <дата>)— достать год, месяц, день, час и т.д. как число.YEAR,MONTH,DAY,HOUR,ISODOW,EPOCH.ISODOW(1=пн, 7=вс) — обычно то, что хочешь, а неDOW(0=вс).EXTRACT(EPOCH FROM ...)— секунды от Unix-epoch или из интервала. Для целого числа —::BIGINT.DATE_TRUNC('month', x)— возвращает timestamp начала периода.WHERE— диапазонная форма (>= '2026-03-01' AND < '2026-04-01') быстрее, чемEXTRACT, потому что использует индекс.UNIX_TIMESTAMP. В SQLite —STRFTIME. В SQL Server —DATEPART.