T-SQL查询出现意外性能问题

时间:2015-08-20 20:55:05

标签: sql sql-server tsql

我有以下T-SQL查询:

SELECT Wk.WeekID,
       Wk.WeekStart,
       SUM(CASE WHEN NOT(Task.WeekDate IS NULL) THEN 1 ELSE 0 END) AS WeekCount,
       SUM(CASE WHEN NOT(Task.DayDate IS NULL) THEN 1 ELSE 0 END) AS DayCount
FROM tblPerWeek AS Wk
LEFT OUTER JOIN tblPerTask AS Task ON (Task.WeekDate = Wk.WeekStart) 
                                   OR (Task.DayDate BETWEEN Wk.WeekStart AND Wk.WeekEnd)
WHERE (Wk.WeekStart <= @DateEnd) AND (Wk.WeekEnd >= @DateStart)
GROUP BY Wk.WeekID, Wk.WeekStart

tblPerWeek表的结构:

WeekID int
WeekStart date
WeekEnd date

tblPerTask表的结构:

TaskID int
WeekDate date
DayDate date

基本上,查询计算分配给周(WeekCount)或周内特定日期(DayCount)的任务。

tblPerWeek有大约2800条记录,tblPerTask有大约70000条记录。

现在,在join中存在(Task.DayDate BETWEEN Wk.WeekStart AND Wk.WeekEnd)条件的问题/故障:

  • 在加入时没有这个条件,查询立即完成
  • 随着 加入查询的这个条件大约需要12秒才能运行

有什么收获?关于如何更快地进行此查询的任何解决方案?

2 个答案:

答案 0 :(得分:1)

通常使用UNION查询来解决使用OR的问题。如果两组记录互斥,则使用UNION ALL甚至更快。

SELECT Wk.WeekID,
       Wk.WeekStart,
       SUM(CASE WHEN NOT(Task.WeekDate IS NULL) THEN 1 ELSE 0 END) AS WeekCount,
       SUM(CASE WHEN NOT(Task.DayDate IS NULL) THEN 1 ELSE 0 END) AS DayCount
FROM tblPerWeek AS Wk
LEFT OUTER JOIN tblPerTask AS Task ON Task.WeekDate = Wk.WeekStart
WHERE (Wk.WeekStart <= @DateEnd) AND (Wk.WeekEnd >= @DateStart)
GROUP BY Wk.WeekID, Wk.WeekStart
UNION 
SELECT Wk.WeekID,
       Wk.WeekStart,
       SUM(CASE WHEN NOT(Task.WeekDate IS NULL) THEN 1 ELSE 0 END) AS WeekCount,
       SUM(CASE WHEN NOT(Task.DayDate IS NULL) THEN 1 ELSE 0 END) AS DayCount
FROM tblPerWeek AS Wk
LEFT OUTER JOIN tblPerTask AS Task ON Task.DayDate BETWEEN Wk.WeekStart AND Wk.WeekEnd
WHERE (Wk.WeekStart <= @DateEnd) AND (Wk.WeekEnd >= @DateStart)
GROUP BY Wk.WeekID, Wk.WeekStart

答案 1 :(得分:1)

优化器讨厌OR

SELECT Wk.WeekID,
       Wk.WeekStart,
       SUM(CASE WHEN TaskW.WeekDate IS NULL THEN 0 ELSE 1 END) AS WeekCount,
       SUM(CASE WHEN TaskD.DayDate  IS NULL THEN 0 ELSE 1 END) AS DayCount
FROM tblPerWeek AS Wk
LEFT OUTER JOIN tblPerTask AS TaskW ON TaskW.WeekDate = Wk.WeekStart                                       
LEFT OUTER JOIN tblPerTask AS TaskD ON TaskD.DayDate BETWEEN Wk.WeekStart AND Wk.WeekEnd
WHERE Wk.WeekStart <= @DateEnd AND Wk.WeekEnd >= @DateStart
GROUP BY Wk.WeekID, Wk.WeekStart

我想你可以做一个计数,因为计数null是0

SELECT Wk.WeekID, Wk.WeekStart,
       COUNT(TaskW.WeekDate) AS WeekCount,
       COUNT(TaskD.DayDate)  AS DayCount
FROM tblPerWeek AS Wk
LEFT OUTER JOIN tblPerTask AS TaskW ON TaskW.WeekDate = Wk.WeekStart                                       
LEFT OUTER JOIN tblPerTask AS TaskD ON TaskD.DayDate BETWEEN Wk.WeekStart AND Wk.WeekEnd
WHERE Wk.WeekStart <= @DateEnd AND Wk.WeekEnd >= @DateStart
GROUP BY Wk.WeekID, Wk.WeekStart