繼從準則: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'標記 –