2016-12-28 3 views
0

Привет здесь я прилагаю мой пример структуры таблицы, которые я хочу использовать в моем проектеНастройка производительности для SQL Query

CREATE TABLE TESTSALESVOLUMETABLE 
(
ID INT IDENTITY(1,1), 
AMOUNT DECIMAL(18,2), 
CREDITEDDATE DATETIME 
) 

и запросов, которые я использовал, как этот

DECLARE @CURRENTDATE AS DATETIME = GETDATE() 
DECLARE @PSV AS INT = 0 
DECLARE @TOTAL AS INT = 0 

IF (DATEPART(DAY, @CURRENTDATE) <= 15) 
BEGIN 
    SELECT @PSV = (
      SELECT Sum(AMOUNT) 
      FROM TESTSALESVOLUMETABLE 
      WHERE DATEPART(DAY, CREDITEDDATE) <= 15 
       AND MONTH(CREDITEDDATE) = MONTH(@CURRENTDATE) 
       AND YEAR(CREDITEDDATE) = YEAR(@CURRENTDATE) 
      ) 
END 
ELSE 
BEGIN 
    SELECT @PSV = (
      SELECT Sum(AMOUNT) 
      FROM TESTSALESVOLUMETABLE 
      WHERE DATEPART(DAY, CREDITEDDATE) > 15 
       AND MONTH(CREDITEDDATE) = MONTH(@CURRENTDATE) 
       AND YEAR(CREDITEDDATE) = YEAR(@CURRENTDATE) 
      ) 
END 

SELECT @total = (
     SELECT Sum(Amount) 
     FROM TESTSALESVOLUMETABLE 
     ) 

SELECT @PSV 'PSV', 
    @total 'TOTAL' 

Есть ли способ чтобы увеличить производительность этого запроса

+0

Тег dbms, который вы используете. (Некоторый специфический SQL-код продукта ...) – jarlh

+0

Добавлен тег 'sql-server' на основе используемого синтаксиса –

ответ

2

Во-первых, вам не нужен подзапрос для установки переменной. Во-вторых, использование функций в столбцах обычно препятствует использованию индексов. Таким образом, я бы рекомендовал что-то вроде этого:

SELECT @PSV = Sum(AMOUNT) 
FROM TESTSALESVOLUMETABLE 
WHERE CREDITEDDATE >= DATEADD(DAY, 1 - DAY(GETDATE()), CAST(GETDATE() as DATE)) AND 
     CREDITEDDATE < DATEADD(DAY, 16 - DAY(GETDATE()), CAST(GETDATE() as DATE)); 

Затем вы хотите индекс на TESTSALESVOLUMETABLE(CREDTEDDATE, AMOUNT).

1

Следуя инструкциям из: 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 
0

я вещь это будет работать нормально

DECLARE @PSV AS INT = 0 
DECLARE @TOTAL AS INT = 0 

IF (DATEPART(DAY,GETDATE()) <= 15) 
BEGIN 
SELECT @PSV = Sum(AMOUNT) 
FROM TESTSALESVOLUMETABLE 
WHERE CREDITEDDATE >= DATEADD(DAY, 1 - DAY(GETDATE()), CAST(GETDATE() as DATE)) AND 
     CREDITEDDATE < DATEADD(DAY, 16 - DAY(GETDATE()), CAST(GETDATE() as DATE)); 
END 
ELSE 
BEGIN 
SELECT @PSV = Sum(AMOUNT) 
FROM TESTSALESVOLUMETABLE 
WHERE CREDITEDDATE >= DATEADD(DAY, 16 - DAY(GETDATE()), CAST(GETDATE() as DATE)) AND 
     CREDITEDDATE < DATEADD(DAY, 31 - DAY(GETDATE()), CAST(GETDATE() as DATE)); 
END 

SELECT @total = (
     SELECT Sum(Amount) 
     FROM TESTSALESVOLUMETABLE 
     ) 

SELECT @PSV 'PSV', 
    @total 'TOTAL'