У меня есть простой SQL-запрос, показанный ниже. Если вы посмотрите на сценарий 1, разница между датами составляет 1 месяц и это правильно (потому что 5 октября — 5 сентября).
Однако, когда вы смотрите на сценарий 2, разница между датами по-прежнему отображается как 1. Но она должна быть 0 (потому что с 4 октября по 5 сентября все еще не 1 месяц).
Можем ли мы убедиться, что сценарий 2 отображается как 0?
Сценарий 1
select
datediff(month, convert(datetime, '05-09-24', 5), convert(datetime, '05-10-24', 5))
1
Сценарий 2
select
datediff(month, convert(datetime, '05-09-24', 5), convert(datetime, '04-10-24', 5))
1
Не так. Здесь DATEDIFF извлекает год и месяц из двух дат и вычисляет разницу. Вычислить дельту месяцев так, как вы это описываете, сложно, потому что продолжительность месяца варьируется. Вы можете извлечь месяц и день отдельно и сравнить их.
DATEDIFF
вычисляет количество пересекаемых границ для данного типа. Таким образом, со 2 сентября по 31 августа — это два дня, но все равно это будет один полный месяц, поскольку он пересекает границу. Кроме того, по историческим причинам ваши литералы даты для значений только даты (без компонента времени) при написании t-sql должны использовать формат yyyyMMdd
(без дефисов). Тогда вы также можете смело использовать cast()
вместо convert()
.
«Но оно должно быть 0 (потому что 4 октября — 5 сентября — это еще не 1 месяц)» Ну, вы указываете параметр как MONTH
. Октябрь-сентябрь = 1, так что это правильно.
Возможно, это немного больше, чем вы ищете, но это можно сократить stackoverflow.com/questions/43126223/…
Это та вещь, которая вам нужна.
SELECT DATEDIFF(month,
CONVERT(DATE, '05/09/24', 5),
CONVERT(DATE, '04/10/24', 5)
) - IIF(
DAY(CONVERT(DATE, '05/09/24', 5)) >
DAY(CONVERT(DATE, '04/10/24', 5)),
1,0
);
Это дает 0.
Я бы использовал выражение регистра и вычитал бы 1, если дата в секундах имеет меньший день.
DECLARE @date1 DateTime
DECLARE @date2 DateTime
SET @date1=convert(datetime,'05-09-24',5); --Italian 5 = dd-mm-yy
SET @date2=convert(datetime,'04-10-24',5);
SELECT CASE WHEN DatePart(day,@date1) < DatePart(day,@date2) THEN
THEN DATEDIFF(month, @date1, @date2)
ELSE DATEDIFF(month, @date1, @date2) -1
END as AbsoltuteDiff
Я считаю, что <
должно быть <=
в приведенном выше примере. Один и тот же начальный/конечный день месяца не требует корректировки. (У вас также есть дополнительный THEN
.)
DATEDIFF() вычисляет количество пересекаемых границ для данного сегмента. Таким образом, период со 2 сентября по 31 августа составляет два дня (дважды пересекает границы дней), но все равно всегда будет одним полным месяцем, поскольку он пересекает границу.
Эта функция возвращает количество (в виде целочисленного значения со знаком) указанных границ частей даты, пересекаемых между указанной начальной датой и конечной датой.
Невозможно заставить его показывать 0 вместо 1, если значения действительно пересекают границу, а использование меньших сегментов, таких как day
, может сбить вас с толку в нечетное время. Единственный вариант — также проверить значение дня обоих входных данных, чтобы решить, хотите ли вы также вычесть 1 из конечного результата.
Если вас интересуют только даты (а не время), вы можете использовать выражение:
DATEDIFF(month, date1, date2)
- CASE WHEN DAY(date1) > DAY(date2) THEN 1 ELSE 0 END
Если вы заботитесь о времени и считаете, что разница между 2024-07-15 16:44:30
и 2024-08-15 16:44:29
меньше одного месяца (на одну секунду), вы можете использовать следующее:
DATEDIFF(month, datetime1, datetime2)
- CASE WHEN DAY(datetime1) > DAY(datetime2)
OR (
DAY(datetime1) = DAY(datetime2)
AND CAST(datetime1 AS TIME) > CAST(datetime2 AS TIME
)
THEN 1 ELSE 0 END
или поочередно:
DATEDIFF(month, datetime1, datetime2)
- CASE WHEN DATEADD(month, DATEDIFF(month, datetime1, datetime2), datetime1)
> datetime2
THEN 1 ELSE 0 END
Каждый из них будет уменьшать разницу месяцев на 1, если первая дата/время в месяце позже второй даты/времени.
Обратите внимание, что для даты/времени начала, приближающихся к концу месяца, когда конечные месяцы короче, месяц не будет засчитан до тех пор, пока дата не перейдет на 1-е число следующего месяца. Например:
2024-01-31 16:45
до 2024-02-29 23:59
считается 0 месяцев.2024-01-31 16:45
по 2024-03-01 00:00
считается 1 месяц.Примеры результатов:
Где:
DiffRaw
— оригинальный DATEDIFF()
расчет.Diff1
— расчет корректировки только за день сверху.Diff2
— это расчет дня + времени сверху.См. эту db<>fiddle для демонстрации.
Добавлено несколько тестовых случаев (все проходят): dbfiddle.uk/rlfHMWnH
Второе значение правильное;
DATEDIFF
сообщает вам, сколько тиков произошло, а не прошло ли так много.DATEDIFF(YEAR,'20231232 23:59:59','20240101 00:00:00')
есть1
, потому что, хотя прошла всего 1 секунда, они в разные годы.