#sql #sql-server #tsql #calculated-columns #leap-year
Вопрос:
У меня есть таблица имен и связанных с ними дат рождения. Я могу получить имена всех, чей день рождения сегодня, сопоставив даты МЕСЯЦА и ДНЯ между датой рождения в базе данных и текущей датой. Тем не менее, мне нужно составить списки «с нетерпением», скажем, всех дней рождения в ближайшие две недели.
Очевидным решением было бы вычислить столбец для каждого человека, показывающий его/ее «день рождения в этом году». Легко извлечь месяц и день из дня рождения, добавить текущий год и привести всю строку в качестве даты. Таким образом, я мог бы просто получить тех, чей «день рождения в этом году» приходится на X дней от текущей даты. Однако у меня есть один человек с днем рождения 29 февраля, и 29 февраля не каждый год, поэтому вычисляемый столбец «захлебывается», когда я запрашиваю или открываю таблицу со следующей ошибкой:
Преобразование типа данных char в тип данных datetime привело к значению datetime вне диапазона.
Предложения о том, как заставить вычисляемый столбец правильно работать в этой ситуации, или альтернативный способ запроса с использованием даты рождения в таблице?
Комментарии:
1. 1) Покажите нам ваш текущий вычисляемый столбец. 2) Зачем вам преобразовывать его в строку? Операции с датами всегда должны храниться в виде дат, что решит вашу проблему.
2. Что вы храните в БД ? Как вы конвертируете в настоящее время ?
Ответ №1:
Измените свой запрос с использования convert
на использование try_convert
, которое не приведет к ошибке в несуществующую дату, а вместо этого вернет значение NULL. Это исключит вашего клиента на день рождения 29 февраля, если рабочий год не является високосным.
Ответ №2:
Ваш подход не сработает в конце декабря, потому что год меняется. Вот другой подход:
- Добавьте количество лет к дате, и пусть база данных обрабатывает високосные годы.
- Затем проведите сравнение.
Итак, логика первого заключается в следующем:
select dateadd(year, year(getdate()) - year(dob), dob)
Затем, чтобы узнать даты рождения в ближайшие две недели:
where dateadd(year, year(getdate()) - year(dob), dob) >= convert(date, getdate()) and
dateadd(year, year(getdate()) - year(dob), dob) < dateadd(14, day, convert(date, getdate())
Однако это все еще не учитывает даты рождения, которые могут быть в следующем году. Итак, чтобы справиться с концом года, подумайте и об этом:
where (dateadd(year, year(getdate()) - year(dob), dob) >= convert(date, getdate()) and
dateadd(year, year(getdate()) - year(dob), dob) < dateadd(14, day, convert(date, getdate())
) or
(dateadd(year, 1 year(getdate()) - year(dob), dob) >= convert(date, getdate()) and
dateadd(year, 1 year(getdate()) - year(dob), dob) < dateadd(14, day, convert(date, getdate())
)
Ответ №3:
Для этого — вам нужно рассчитать DOB текущего года и следующую дату рождения человека. Для расчета текущего года даты рождения мы можем использовать простой расчет:
dateadd(year, datediff(year, DateOfBirth, CurrentDate), DateOfBirth)
Затем — нам нужно рассчитать следующую дату рождения, которая просто добавляет год, если текущий год DOB меньше текущей даты:
dateadd(year, iif(CurrentDOB < CurrentDate, 1, 0), CurrentDOB)
Теперь — это просто вопрос проверки, находится ли следующий DOB в диапазоне. Вот некоторые примеры данных, чтобы показать, как это объединить.
--==== Some sample dates of birth - including leap year DOB's
Declare @testData Table (DateOfBirth date);
Insert Into @testData (DateOfBirth)
Values ('1992-01-09'), ('2020-02-29'), ('1965-09-30'), ('1984-02-29');
--==== Test using different dates
Declare @current_date date = '2021-02-28';
--==== Use CROSS APPLY to calculate current year DOB and Next DOB
Select *
From @testData As td
Cross Apply (Values (dateadd(year, datediff(year, td.DateOfBirth, @current_date), td.DateOfBirth))) As y(CurrentDOB)
Cross Apply (Values (dateadd(year, iif(y.CurrentDOB < @current_date, 1, 0), y.CurrentDOB))) As n(NextDOB)
Where n.NextDOB <= dateadd(day, 14, @current_date);
Если сегодня 2021-02-28, то будут включены дни рождения, которые выпадают на 29-е число. Если сегодня 2021-03-01, то они не будут включены, потому что они будут рассчитаны как 2022-02-28, то есть не в течение 14 дней.