为什么在DateAdd中使用硬编码分钟比使用字段值更快?

时间:2016-01-25 01:59:05

标签: sql-server sql-server-2008

处理SQL问题并且我不是SQL人员,因此需要一些指导。

鉴于下面的SQL语句,请注意第一个在DateAdd函数中使用硬编码值“-360”,而第二个使用每个记录上存在的字段值(OFFSET)(其值为“-360”或“-300”取决于DST时间。)

运行第一个查询非常快,而第二个查询需要大约40秒。

有人可以告诉我,执行第二次这么长时间的区别是什么,因为我必须使用该记录的值而不是硬编码,我怎样才能加快查询速度?

查询1(快速):

SELECT 0 AS 'TempIndex', COUNT(*) AS 'TotalLY'
FROM CLOGS15 h 
WHERE h.EVTYPE = 1 
AND DateAdd(minute, -360, h.EVDATE) BETWEEN '2015-01-01 00:00:00.000' AND '2015-01-24 00:00:00.000'

查询2(SLOW):

SELECT 0 AS 'TempIndex', COUNT(*) AS 'TotalLY'
FROM CLOGS15 h 
WHERE h.EVTYPE = 1 
AND DateAdd(minute, OFFSET, h.EVDATE) BETWEEN '2015-01-01 00:00:00.000' AND '2015-01-24 00:00:00.000'

2 个答案:

答案 0 :(得分:2)

我只能想象问题是sargability(索引的用户)。但是,我认为dateadd()会阻止使用索引。如果你想解决这个问题,也许这会有效:

SELECT 0 AS TempIndex, COUNT(*) AS TotalLY
FROM CLOGS15 h 
WHERE h.EVTYPE = 1 AND offset = 360 AND
      DateAdd(minute, -360, h.EVDATE) BETWEEN '2015-01-01' AND '2015-01-24'
UNION ALL
SELECT 0 AS TempIndex, COUNT(*) AS TotalLY
FROM CLOGS15 h 
WHERE h.EVTYPE = 1 AND offset = 300 AND
      DateAdd(minute, -300, h.EVDATE) BETWEEN '2015-01-01' AND '2015-01-24';

编辑:

糟糕,上面返回两行,你想要一行。所以,使用子查询:

SELECT TempIndex, SUM(TotalLY) as TotalLY
FROM (SELECT 0 AS TempIndex, COUNT(*) AS TotalLY
      FROM CLOGS15 h 
      WHERE h.EVTYPE = 1 AND offset = 360 AND
            DateAdd(minute, -360, h.EVDATE) BETWEEN '2015-01-01' AND '2015-01-24'
      UNION ALL
      SELECT 0 AS TempIndex, COUNT(*) AS TotalLY
      FROM CLOGS15 h 
      WHERE h.EVTYPE = 1 AND offset = 300 AND
            DateAdd(minute, -300, h.EVDATE) BETWEEN '2015-01-01' AND '2015-01-24'
    ) h
GROUP BY TempIndex;

答案 1 :(得分:0)

在您的情况下,我认为快速和慢速查询之间的区别在于索引的使用。

快速查询中的SQL Server可能会重写您的DATEADD以启用EVDATE上的索引使用。当您添加日期和常量并检查它是否在常量日期之间时,它可能会将DATEADD从左侧(BETWEEN之前)移动到右侧(之后的日期) BETWEEN,反转DATEADD中常量的信号。

您的原件是:

DateAdd(minute, -360, h.EVDATE) 
  BETWEEN '2015-01-01 00:00:00.000' 
      AND '2015-01-24 00:00:00.000'

可能会转向:

h.EVDATE 
  BETWEEN DateAdd(minute, 360, '2015-01-01 00:00:00.000') 
      AND DateAdd(minute, 360, '2015-01-24 00:00:00.000')

这与原始过滤器相同,但启用了DATEADD的索引使用和处理,因为只需要处理两个日期,而不是表格中的所有日期。

在慢速查询中,由于DATEADD正在使用变量偏移量,因此SQL Server无法应用上述相同规则,因此它会使用DATEADD处理表格中的所有日期,这真的很慢。

我认为您应该尝试像快速查询这样的过滤器,可能是这样的:

SELECT 0 AS 'TempIndex', COUNT(*) AS 'TotalLY'
FROM CLOGS15 h 
WHERE 1=1 
  AND h.EVTYPE = 1 
  AND 
  (0=1
    OR
    (1=1
      AND OFFSET = -360
      AND h.EVDATE >= DateAdd(minute, 360, '2015-01-01 00:00:00.000') 
      AND h.EVDATE <= DateAdd(minute, 360, '2015-01-24 00:00:00.000')
    )
    OR
    (1=1
      AND OFFSET = -300
      AND h.EVDATE >= DateAdd(minute, 300, '2015-01-01 00:00:00.000') 
      AND h.EVDATE <= DateAdd(minute, 300, '2015-01-24 00:00:00.000')
    )
  )

查询分别包含EVTYPEOFFSETEVDATE列的索引会很棒。