带有日期的SUM和Where子句

时间:2015-09-10 12:25:20

标签: sql sql-server

我有两个查询都给出相同的结果,但一个比另一个

更快

查询一个

SELECT 
    MP.MilestonePlanningID, SUM(TS.UnitsUsed) TotalUnits
FROM 
    MilestonePlanning MP
INNER JOIN 
    Timesheet TS ON MP.MilestonePlanningID = TS.MilestonePlanningID
INNER JOIN 
    PlanningAction PA ON MP.LastPlanningActionID = PA.ActionID
WHERE 
    PA.ActionNameID = 4 
    AND PA.ActionDateTime >= DATEADD(MONTH,-1,GETDATE())
GROUP BY 
    MP.MilestonePlanningID

查询两个

SELECT MP.MilestonePlanningID, SUM(TS.UnitsUsed) TotalUnits
FROM MilestonePlanning MP
    INNER JOIN Timesheet TS ON MP.MilestonePlanningID = TS.MilestonePlanningID
    INNER JOIN PlanningAction PA ON MP.LastPlanningActionID = PA.ActionID
WHERE PA.ActionNameID = 4
  AND CAST(PA.ActionDateTime AS TIMESTAMP) >= CAST(DATEADD(MONTH,-1,GETDATE()) AS TIMESTAMP)
GROUP BY MP.MilestonePlanningID

查询一个是最明显的一个,它是我开始的那个但是它需要大约9到15秒才能执行,但是当我删除以下内容时它仍然是即时的

AND PA.ActionDateTime >= DATEADD(MONTH,-1,GETDATE())

查询二是即时的,没有以下行

AND CAST(PA.ActionDateTime AS TIMESTAMP) >= CAST(DATEADD(MONTH,-1,GETDATE()) AS TIMESTAMP)

查询二也不是查看上个月到今天的数据最明显的方式。任何人都可以向我解释这里发生了什么,以及我在查询一中做错了什么

附加执行计划ExecutionPlan

1 个答案:

答案 0 :(得分:0)

没有执行计划就不能肯定地说,但我的猜测是统计数据有点过时,as can happen with ascending keys,所以基数估计是错误的。当您包含此行时:

PA.ActionDateTime >= DATEADD(MONTH,-1,GETDATE())

我怀疑SQL Server确定将返回很少的行,因此选择基于此的计划,可能是某个地方的嵌套循环连接,或书签查找,其中应使用散列/合并生成更广泛的计划分别加入或聚集索引扫描。

第二个查询在有和没有日期过滤器的情况下立即工作的原因是因为转换为时间戳,基本上是转换为二进制,这意味着SQL Server无法使用ActionDateTime上的统计信息,因此被迫进入更广泛的计划,因为它无法确定匹配行数的合理估计。

修改

看到这些计划后,我的怀疑得到了证实。基数估计已经结束,SQL服务器假定上个月PlanningActionActionDateTimePlanningAction的行数很少,因此确定最佳 获取行的方法是从MilestonePlanning获取所有匹配的行,然后对于这些行中的每一行进入另外两个表并获取数据。对于几行,这比首先从Timesheet获取数据更有效 MERGE JOIN然后删除大部分数据。对于很多行来说,这是非常低效的。

没有where子句SQL Server意识到这将是一个糟糕的计划选择,因此使用哈希匹配而不是嵌套循环连接,这更适合处理更多的数据行。

修复方法是简单地更新统计信息,并可能创建维护计划以确保更频繁地更新统计信息。或者强制SELECT MP.MilestonePlanningID, SUM(TS.UnitsUsed) TotalUnits FROM MilestonePlanning MP INNER MERGE JOIN Timesheet TS ON MP.MilestonePlanningID = TS.MilestonePlanningID INNER MERGE JOIN PlanningAction PA ON MP.LastPlanningActionID = PA.ActionID WHERE PA.ActionNameID = 4 AND PA.ActionDateTime >= DATEADD(MONTH,-1,GETDATE()) GROUP BY MP.MilestonePlanningID;

DATEADD

你应谨慎使用它,因为有人可能会出现并且只想查询最后一小时,其中嵌套循环连接可能更有效,但只更改ctrl+a位,你可能再次拥有性能问题。

您还可以使用索引来协助查询,但如果它们已经是即时的,则可能不值得拥有索引的额外维护成本。