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表示两年中的同一个月和同一天,但是没有办法对这些数据进行索引,以进行查询,从而知道(闰日也确实会把事情搞砸),您可以在这方面使用andJOIN
。确保您的日历包含月数和日数列,并且包含的查询,其中CT.MonthNum=@MonthValue和CT.DayNum=@DayValue
是完全可搜索的。(CT
是日历表)。使用日历表,您可以添加一个额外的列来处理闰年,例如LeapDay
,每个日期的日期与day
相同,2月29日除外,其中包含28
,isleappyear
,以指示闰年etc@PanagiotisKanavos这也行。:)CASE
表达式只是一个例子,因为链接的文章更像是对日历表的介绍。