2016-12-28 16 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' 
相关问题