继从准则:Bad habits to kick : mis-handling date/range queries - Aaron Bertrand - 2009-10-16
首先,我们要摆脱:
where datepart(day, crediteddate) <= 15
and month(crediteddate)=month(@currentdate)
and year(crediteddate)=year(@currentdate)
因为:
[...]你已经有效地消除的可能性SQL Server利用索引的优势。既然你已经强迫它建立一个不可调整的条件,这意味着它将不得不转换表中的每一个值,以便将它与你在右边呈现的[值]进行比较[...]
其次,我们要确保避免使用between
与日期时间,因为它可以返回不需要的行或错过想要的行,即使使用类似between ... and dateadd(second, -1, @thrudate)
或甚至between ... and 'yyyy-mm-ddT23:59:59.997'
。 (有关更多示例,请参阅Aaron Bertrand's article)。
因此,要做到这一点,最好的办法是说:
而且,戈登·利诺夫提到,Y ou将受益于testsalesvolumetable(crediteddate, amount)
的指数。但戈登的公式总是返回当前月份的第一和第十六。
根据当天的情况,我们可以将过程分成两个查询,而不是根据当前日期计算这些查询,只需使用一个查询即可。
以下是使用和不使用变量的from和thru日期的示例代码,以及用于检查结果范围的快速日历测试。
rextester链路测试设置:http://rextester.com/YVLI65217
create table testsalesvolumetable (crediteddate datetime not null, amount int not null)
insert into testsalesvolumetable values
('20161201',1) ,('20161202',1) ,('20161203',1) ,('20161204',1) ,('20161205',1)
,('20161206',1) ,('20161207',1) ,('20161208',1) ,('20161209',1) ,('20161210',1)
,('20161211',1) ,('20161212',1) ,('20161213',1) ,('20161214',1) ,('20161215',1)
,('20161216',1) ,('20161217',1) ,('20161218',1) ,('20161219',1) ,('20161220',1)
,('20161221',1) ,('20161222',1) ,('20161223',1) ,('20161224',1) ,('20161225',1)
,('20161226',1) ,('20161227',1) ,('20161228',1) ,('20161229',1) ,('20161230',1)
,('20161231',1) ,('20170101',1)
/* ----- without variables */
declare @psv int;
select @psv = Sum(amount)
from testsalesvolumetable
where crediteddate >= dateadd(day, (1- (day(convert(date,getdate()))/16)) - (day(convert(date,getdate()))%16), convert(date,getdate()))
and crediteddate < case
when day(convert(date,getdate()))>15
then dateadd(month, datediff(month, -1, convert(date,getdate())), 0)
else dateadd(day,15,dateadd(month, datediff(month, 0, convert(date,getdate())), 0))
end;
select [email protected];
--*/
/* ----- with variables */
--declare @psv int;
declare @currentdate date;
/* change to date datatype to get rid of time portion*/
set @currentdate = getdate();
--set @currentdate = '20161212'
declare @fromdatetime datetime;
declare @thrudatetime datetime;
set @fromdatetime = dateadd(day, (1- (day(@currentdate)/16)) - (day(@currentdate)%16), @currentdate);
set @thrudatetime = case
when day(@currentdate)>15
then dateadd(month, datediff(month, -1, @currentdate), 0)
else dateadd(day,15,dateadd(month, datediff(month, 0, @currentdate), 0))
end;
select @psv = sum(amount)
from testsalesvolumetable
where crediteddate >= @fromdatetime
and crediteddate < @thrudatetime;
--/*
select
[email protected]
, CurrentDate =convert(varchar(10),@currentdate ,121)
, FromDateTime=convert(varchar(10),@fromdatetime,121)
, ThruDateTime=convert(varchar(10),@thrudatetime,121);
--*/
Rextester链接,日历测试:http://rextester.com/ESZRH30262
--/* ----- Calendar Test */
;with n as (
select n from (values(1),(2),(3),(4),(5),(6),(7),(8),(9),(10)) t(n)
)
, cal as (
select DateValue=convert(datetime,dateadd(day, row_number() over (order by (select 1)) -1, '20160101'))
from n as a
cross join n as b
cross join n as c
cross join n as d
)
select
--DateValue=convert(varchar(10),DateValue,121)
minDate =convert(varchar(10),min(DateValue),121)
, maxDate =convert(varchar(10),max(DateValue),121)
, FromDatetime=convert(varchar(10),dateadd(day, (1- (day(DateValue)/16)) - (day(DateValue)%16), DateValue),121)
, ThruDatetime=convert(varchar(10),case
when day(DateValue)>15
then dateadd(m, datediff(m, -1, DateValue), 0)
else convert(varchar(10),dateadd(day, 16 - day(DateValue), DateValue),121)
end,121)
, GordonFrom = convert(varchar(10),dateadd(day, 1 - day(DateValue), cast(DateValue as date)),121)
, GordonThru = convert(varchar(10),dateadd(day, 16 - day(DateValue), cast(DateValue as date)),121)
from cal
where datevalue >= '20160101'
and datevalue < '20170101'
--/*
group by
convert(varchar(10),dateadd(day, (1- (day(DateValue)/16)) - (day(DateValue)%16), DateValue),121)
, convert(varchar(10),case
when day(DateValue)>15
then dateadd(m, datediff(m, -1, DateValue), 0)
else convert(varchar(10),dateadd(day, 16 - day(DateValue), DateValue),121)
end,121)
, convert(varchar(10),dateadd(day, 1 - day(DateValue), cast(DateValue as date)),121)
, convert(varchar(10),dateadd(day, 16 - day(DateValue), cast(DateValue as date)),121)
order by FromDateTime
标记您正在使用的dbms。 (某些产品特定的SQL在那里...) – jarlh
根据所使用的语法添加了'sql-server'标记 –