Следуя инструкциям из: 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).
Так что лучший способ сделать это было бы сказать:
Если сегодня является 15 или выше, получить строки> = 1-го числа этого месяца и < 16го этого месяца
Если сегодня 16-й или более поздней версии, получить строки> = 16-й в этом месяце и < 1-го числа следующего месяца
Кроме того, как уже упоминалось Гордон Линофф, у ou выиграет от индекса на testsalesvolumetable(crediteddate, amount)
. Но формулы Гордона всегда возвращают 1 и 16 числа текущего месяца.
Вместо того, чтобы нарушать процедуру на два запроса в зависимости от текущего дня, мы можем вычислить их из дат и использовать даты и просто использовать один запрос.
Вот пример кода с использованием и без использования переменных для дат и дней, а также быстрый календарь для проверки полученных диапазонов.
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' на основе используемого синтаксиса –