Сравнение дат в Transact-SQL

#sql #tsql

#sql #tsql

Вопрос:

Почему сравнение работает странно в этом примере? Даты, которые являются днями рождения, являются:

 1990-03-22
1998-03-20
1990-03-22
2002-12-02
2004-03-18
2004-03-20
2004-03-25 
 

Я делаю это

 WHERE DATEADD(YY, 18, jun.birth_date) >= DATEADD(YY, 1, GETDATE())
 

Который, я думаю, должен отсечь все записи людей старше 18 лет через год с сегодняшнего дня. Но это абсолютно противоположно, вот что я получаю:

 2004-03-18
2004-03-20
2004-03-25 
 

И я получаю правильные результаты, когда я просматриваю записи меньше и даже больше, чем дата в следующем году.
Почему это так работает?

Комментарии:

1. Если вам нужны люди, которым сегодня исполнилось 18 лет или больше, зачем добавлять год к GETDATE ?

2. На английском языке этот SQL говорит; где 18-й день рождения — это год или больше в будущем.

Ответ №1:

Это то, что у вас есть…

 WHERE
    someone's date of birth   18 years
    >=
    Today's date   1 year
 

На английском языке это; where the 18th birthday is a year or more in the future.

Я думаю, что вы действительно хотите (если вам нужны люди старше 18 лет)… WHERE the birthdate is earlier than today's date minus 18 years (18 или более лет назад)

 WHERE
    Jun.birth_date <= DATEADD(YY, -18, GETDATE())
 

Ответ №2:

Если вам нужны люди, которым сегодня исполнилось 18 лет или больше, то сегодня отнимите 18 лет, не добавляйте годы в столбец DoB, так как это делает запрос не подлежащим обработке:

 SELECT birth_date
FROM dbo.YourTable
WHERE birth_date <= DATEADD(YEAR, -18, CONVERT(date,GETDATE()));
 

Ответ №3:

Почему вы не используете datediff , вместо dateadd ?

 with d as (
  select cast('1990-03-22' as date) as birthDate union all
  select cast('1998-03-20' as date) as birthDate union all
  select cast('1990-03-22' as date) as birthDate union all
  select cast('2002-12-02' as date) as birthDate union all
  select cast('2004-03-18' as date) as birthDate union all
  select cast('2004-03-20' as date) as birthDate union all
  select cast('2004-03-25' as date) as birthDate 
)
select *
from d
where datediff(year,birthDate, getdate()) >= 18
 

Так проще понять поведение.

Комментарии:

1. «Почему бы вам не использовать datediff , вместо dateadd потому что это все еще невозможно.

2. @Larnu, насколько я вижу в вопросе, это не проблема. Если это не так, для меня гораздо проще понять этот запрос, используя datediff вместо dateadd .

3. Применение функций к искомому столбцу — плохая привычка. Однажды наученный, трудно отучить. Я согласен, что это функционально правильно и НЕМНОГО проще для понимания, но что касается SQL, я считаю, что это все еще анти-шаблон, и поэтому я согласен с Larnu. Born on or before 18 years ago вряд ли это сложная концепция, даже если Age >= 18 ЭТО тривиальная концепция.

4. Привет @MatBailie. Я понимаю, почему лучше избегать использования функций для поиска по столбцу, но я только что сказал, что в зависимости от размера данных это может быть проблемой или нет. И если запрос легче понять, возможно, оправдано его оплатить, учитывая, что стоимость может быть нулевой.

5. Чтобы привести ссылку на @JaimeDrq: From datediff : » int Разница между начальной и конечной датой, выраженная в границе, установленной datepart «. В этом случае границей является изменение года, например, 2020-12-31 и 2021-01-01 разделены одним годом. 2020-01-01 и 2021-12-31 также разделены одним годом.