Текущая дата минус день, месяц или год в SQL

В SQL Server из даты вычитают функцией DATEADD с отрицательным числом. Отдельной DATESUB в T-SQL нет:

SELECT DATEADD(day,    -1, GETDATE());  -- сейчас минус 1 день
SELECT DATEADD(month,  -1, GETDATE());  -- сейчас минус 1 месяц
SELECT DATEADD(year,   -1, GETDATE());  -- сейчас минус 1 год
SELECT DATEADD(minute, -30, GETDATE()); -- сейчас минус 30 минут

Синтаксис: DATEADD(datepart, number, date) (документация Microsoft). С положительным числом дата сдвигается вперёд: DATEADD(month, 5, GETDATE()) — дата через пять месяцев.

Разницу между двумя датами считает DATEDIFF(day, @from, @to): это число дней между ними. Функция считает переходы через границу периода, а не полные периоды, поэтому DATEDIFF(year, '20251231', '20260101') вернёт 1, хотя прошёл один день.

Вчерашняя дата без времени

GETDATE() возвращает дату вместе со временем. Чтобы получить вчерашнюю дату, сначала приведите значение к date:

-- сегодня, без времени
SELECT CAST(GETDATE() AS date);

-- вчера
SELECT DATEADD(day, -1, CAST(GETDATE() AS date));

-- вчера, SQL Server 2025
SELECT DATEADD(day, -1, CURRENT_DATE);

С SQL Server 2025 в T-SQL есть функция CURRENT_DATE: она делает то же, что CAST(GETDATE() AS date), и возвращает date (документация Microsoft). Есть она и в Azure SQL Database, и в Azure SQL Managed Instance. В SQL Server 2022 и более ранних версиях её нет: 2022 отвечает ошибкой «Incorrect syntax near the keyword 'CURRENT_DATE'».

DATEADD возвращает тот же тип, что получил: datetime для GETDATE(), date для date. Строку SQL Server читает как datetime, поэтому пишите её в формате 'yyyymmdd': если язык сеанса русский, немецкий или британский, '2026-11-03' превратится в 11 марта.

GETDATE минус 1 день в WHERE

Записи за последние 24 часа:

SELECT * FROM Orders
WHERE CreatedAt >= DATEADD(day, -1, GETDATE());

Записи за вчера, с полуночи до полуночи:

SELECT * FROM Orders
WHERE CreatedAt >= DATEADD(day, -1, CAST(GETDATE() AS date))
  AND CreatedAt <  CAST(GETDATE() AS date);

Столбец не оборачивайте в функции, арифметику держите по другую сторону от знака сравнения. Я сравнил планы на SQL Server 2022 с индексом по CreatedAt: такой диапазон даёт Index Seek, а DATEDIFF(day, CreatedAt, GETDATE()) = 1 и CONVERT(varchar, CreatedAt, 120) читают индекс целиком. CAST(CreatedAt AS date) = ... — исключение, с ним SQL Server тоже ищет по индексу. Но полуоткрытый интервал >= начало AND < конец одинаково работает в любой СУБД.

С переменной DECLARE @today date индекс может не сработать: оптимизатор строит план, не зная её значения, и считает, что под условие попадёт фиксированная доля таблицы. В моём тесте SELECT * с переменной прочитал весь кластерный индекс. Если переменная нужна, допишите OPTION (RECOMPILE).

Не закрывайте день условием BETWEEN ... AND '23:59:59.999': при сравнении с datetime строка '23:59:59.999' округлится до следующей полуночи, и в выборку попадут строки ровно на 00:00:00 сегодняшнего дня.

Текущая дата минус год или месяц

-- тот же день год назад
SELECT DATEADD(year, -1, CAST(GETDATE() AS date));

SELECT DATEADD(month, -1, '20260331'); -- 2026-02-28 00:00:00.000
SELECT DATEADD(year, -1, '20280229');  -- 2027-02-28 00:00:00.000

Если такого числа в нужном месяце нет, DATEADD вернёт последний день месяца.

В SQL Server 2022 появилась функция DATETRUNC, она считает начало месяца:

-- начало этого месяца
SELECT DATETRUNC(month, GETDATE());

-- начало прошлого месяца
SELECT DATEADD(month, -1, DATETRUNC(month, GETDATE()));

-- последний день прошлого месяца
SELECT EOMONTH(GETDATE(), -1);

В старых версиях берите связку DATEADD и DATEDIFF из поста про начало месяца в MS SQL.

GETDATE, SYSDATETIME или CURRENT_TIMESTAMP

  • CURRENT_TIMESTAMP — ANSI-имя для GETDATE(): тот же datetime с точностью около 3 мс. Текущий timestamp минус день — DATEADD(day, -1, CURRENT_TIMESTAMP).
  • SYSDATETIME() возвращает datetime2(7), у секунд в нём семь знаков после запятой.
  • GETUTCDATE() и SYSUTCDATETIME() возвращают время в UTC. Azure SQL Database по умолчанию работает в UTC, поэтому там и GETDATE() вернёт UTC.

Значения datepart

  • year (yy, yyyy) — год
  • quarter (qq, q) — квартал
  • month (mm, m) — месяц
  • dayofyear (dy, y) — день года
  • day (dd, d) — день
  • week (wk, ww) — неделя
  • weekday (dw, w) — день недели
  • hour (hh) — час
  • minute (mi, n) — минута
  • second (ss, s) — секунда
  • millisecond (ms) — миллисекунда
  • microsecond (mcs) — микросекунда
  • nanosecond (ns) — наносекунда

В DATEADD значения dayofyear, day и weekday одинаково прибавляют дни. microsecond и nanosecond не работают с datetime, а значит и с GETDATE().

Текущая дата минус день в PostgreSQL, MySQL, SQLite и Oracle

Запросы для PostgreSQL 16, MySQL 8.4 и SQLite 3.51 я прогнал сам, Oracle сверил с документацией.

PostgreSQL

SELECT CURRENT_DATE - 1;                  -- сегодня минус день
SELECT now() - interval '1 day';          -- сейчас минус сутки
SELECT CURRENT_DATE - interval '1 month'; -- сегодня минус месяц

Дата минус интервал даёт timestamp: CURRENT_DATE - INTERVAL '1 day' — это вчерашняя полночь. Нужна дата — допишите ::date.

MySQL

SELECT DATE_SUB(CURDATE(), INTERVAL 1 DAY);   -- сегодня минус день
SELECT NOW() - INTERVAL 1 DAY;                -- сейчас минус сутки
SELECT DATE_SUB(CURDATE(), INTERVAL 1 MONTH); -- сегодня минус месяц

Не пишите CURDATE() - 1: MySQL превратит дату в число, и 1 ноября получится 20261100.

SQLite

SELECT date('now', '-1 day');     -- сегодня минус день
SELECT datetime('now', '-1 day'); -- сейчас минус сутки
SELECT date('now', '-1 month');   -- сегодня минус месяц

'now' — это UTC, для местного времени добавьте 'localtime'. date('2026-03-31', '-1 month') вернёт 2026-03-03. С версии 3.46 добавьте последним аргументом 'floor' и получите 2026-02-28.

Oracle

SELECT TRUNC(SYSDATE) - 1 FROM dual;             -- сегодня минус день
SELECT SYSDATE - 1 FROM dual;                    -- сейчас минус сутки
SELECT ADD_MONTHS(TRUNC(SYSDATE), -1) FROM dual; -- сегодня минус месяц

Число здесь означает дни, поэтому SYSDATE - 1/24 — час назад. Год назад — ADD_MONTHS(SYSDATE, -12). SYSDATE - INTERVAL '1' MONTH 31 марта упадёт с ошибкой: 31 февраля не бывает.

Где ошибаются в T-SQL

  • GETDATE() - 1 работает с datetime, а SYSDATETIME() - 1 падает с ошибкой «Operand type clash: datetime2 is incompatible with int». Берите DATEADD.
  • CURRENT_DATE - 1 работает в PostgreSQL, но в T-SQL к date нельзя прибавить число: SQL Server 2025 выдаст ошибку «Operand type clash: date is incompatible with int». Пишите DATEADD(day, -1, CURRENT_DATE).
  • m — это месяц, а не минута. Минута — mi или n.
  • datepart нельзя передать в переменной: DATEADD(@part, -1, GETDATE()) падает с ошибкой «Invalid parameter 1 specified for dateadd». Варианты перебирайте в CASE.
Консультирую по продукту, собираю MVP и учу делать продукты самостоятельно. Поработать со мной →