SQL Server:仅比较月份和日期-SARGable

SQL Server:仅比较月份和日期-SARGable,sql,sql-server,datetime,query-performance,Sql,Sql Server,Datetime,Query Performance,我有一个表存储一个datetime列,该列被索引。我试图找到一种只比较月份和日期的方法(完全忽略年份) 为了记录在案,我想说我已经在使用和。但是我遇到了一个问题,我当前的实现使用索引扫描而不是索引查找,因为两个函数都直接使用列来获取月份和日期 可以有两种类型的引用进行比较:固定的给定日期和今天(GETDATE())。日期将根据时区进行转换,然后提取其月份和日期,例如 DECLARE @monthValue DATETIME = MONTH(@ConvertDateTimeFromServer_T

我有一个表存储一个
datetime
列,该列被索引。我试图找到一种只比较月份和日期的方法(完全忽略年份)

为了记录在案,我想说我已经在使用和。但是我遇到了一个问题,我当前的实现使用索引扫描而不是索引查找,因为两个函数都直接使用列来获取月份和日期

可以有两种类型的引用进行比较:固定的给定日期和今天
(GETDATE())
。日期将根据时区进行转换,然后提取其月份和日期,例如

DECLARE @monthValue DATETIME = MONTH(@ConvertDateTimeFromServer_TimeZone);
DECLARE @dayValue DATETIME = DAY(@ConvertDateTimeFromServer_TimeZone); 
另一点是,该列存储不同年份的datetime,例如

1989-06-21 00:00:00.000
1965-10-04 00:00:00.000
1958-09-15 00:00:00.000
1965-10-08 00:00:00.000
1942-01-30 00:00:00.000
现在问题来了。如何创建一个SARGable查询来获取表中与给定月份和日期匹配的行,而不考虑年份,但也不涉及任何函数中的列?网络上的现有示例使用了年份和/或日期范围,这对我的案例毫无帮助

示例查询:

Select t0.pk_id 
From dob t0 WITH(NOLOCK) 
WHERE ((MONTH(t0.date_of_birth) = @monthValue AND DAY(t0.date_of_birth) = @dayValue))

我也尝试过
DATEDIFF()
DATEADD()
,但它们都以索引扫描结束。

在我的评论中添加了

这可能是获得可搜索查询的最简单方法。正如您所发现的,
MONTH([YourColumn])
DATEPART(MONTH,[YourColumn])
都会导致查询变得不可搜索

考虑到您的所有列(至少在示例数据中)的时间都是
00:00:00
,这对我们有利,因为它们实际上只是日期。这意味着我们可以轻松地
使用以下方法将
加入日历表:

SELECT dob.[YourColumn]
FROM dob
     JOIN CalendarTable CT ON dob.DateOfBirth = CT.CalendarDate;
现在,如果我们使用上述文章中的表,您将创建一些额外的列(
MonthNo
CDay
,但是,您可以随意调用它们)。然后,可以将这些列添加到查询中:

SELECT dob.[YourColumn]
FROM dob
     JOIN CalendarTable CT ON dob.DateOfBirth = CT.CalendarDate
WHERE CT.MonthNo = @MonthValue
  AND CT.CDay = @DayValue;
如您所见,这是一个更适合搜索的查询

如果您想处理闰年,可以使用
CASE
表达式添加更多的逻辑:

SELECT dob.[YourColumn]
FROM dob
     JOIN CalendarTable CT ON dob.DateOfBirth = CT.CalendarDate
WHERE CT.MonthNo = @MonthValue
  AND CASE WHEN DATEPART(YEAR, GETDATE()) % 4 != 0 AND CT.CDat = 29 AND CT.MonthNo = 2 THEN 28 ELSE CT.Cdat END = @DayValue;
这将某人2月29日的生日视为非闰年的2月28日(当
DATEPART(YEAR,GETDATE())%4!=0时)

另外,可能值得注意的是,将您的DateOfBirth列更改为
日期时可能值得一提。出生日期不是在给定的时间,只是在给定的日期;这意味着日历表上没有从
datetime
date
的隐式转换


编辑:另外,刚刚注意到,为什么要使用
NOLOCK
?你知道那是什么,对吧。。?除非您对脏读和重影数据感到满意?

您是否能够向表中添加一个计算列,以存储“正常化”日期(例如,它与当前列相同,但所有日期都在2000年内)?@Damien_不相信这是唯一想到的选项,选择闰年很好。@Damien_不相信这一点,因为生产表的大小(大约2000万行)以及它也是用户生成的表,所以这样做是不可行的。不幸的是,
datetime
(或datetime2)是过去某个固定日期之后的间隔计数。假设从1900年起已经天了(对于
datetime
的整数部分)。42778和43143表示两年中的同一个月和同一天,但是没有办法对这些数据进行索引,以进行查询,从而知道(闰日也确实会把事情搞砸),您可以在这方面使用and
JOIN
。确保您的日历包含月数和日数列,并且包含
的查询,其中CT.MonthNum=@MonthValue和CT.DayNum=@DayValue
是完全可搜索的。(
CT
是日历表)。使用日历表,您可以添加一个额外的列来处理闰年,例如
LeapDay
,每个日期的日期与
day
相同,2月29日除外,其中包含
28
isleappyear
,以指示闰年etc@PanagiotisKanavos这也行。:)
CASE
表达式只是一个例子,因为链接的文章更像是对日历表的介绍。