2016-12-28 63 views
0

嗨性能調整,我想在我的項目的SQL查詢我在這裏附上我的樣本表結構

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日

而且,戈登·利諾夫提到,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 
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' 
相關問題