我有下面的查询,这证明是非常昂贵的,需要6-8秒才能执行。从执行计划来看,排序操作的成本是79%。我能在这里得到任何改进吗?

SELECT
A.StageName,
C.Month,
C.MonthName as Label,
C.Year,
isnull(A.Average,0) as Data
FROM
(
SELECT
S.StageName,
MONTH(TimeIn) as MonthNumber,
DATENAME(MONTH,TimeIn) as Month,
YEAR(TimeIn) as Year,
ISNULL(AVG(DATEDIFF(mi,TimeIn,isnull(TimeOut,@TodayDate))),0) as Average
FROM
VisitMovement VM
INNER JOIN Stage S on
VM.StageID = S.StageID
WHERE
(VM.TimeIn >= @StartDate AND
VM.TimeIn < DATEADD (d,1,@EndDate)) AND
(VM.TimeOut < DATEADD (d,1,@EndDate) OR VM.TimeOut IS NULL)
GROUP BY
S.StageNumber,
S.StageName,
MONTH(TimeIn),
DATENAME(MONTH,TimeIn),
YEAR(TimeIn)
) A
RIGHT JOIN (select distinct Month,MonthName,Year from Calendar WHERE DATE >= @StartDate AND DATE < DATEADD (d,1,@EndDate)) C on
A.MonthNumber = C.Month and
A.Month = C.MonthName and
A.Year = C.Year
GROUP BY
A.StageName,
C.Month,
C.MonthName,
C.Year,
A.Average
ORDER BY
CASE WHEN @Ordering = 'asc' THEN C.Year END ASC,
CASE WHEN @Ordering = 'asc' THEN C.Month END ASC,
CASE WHEN @Ordering = 'asc' THEN A.StageName END ASC,
CASE WHEN @Ordering = 'desc' THEN C.Year END DESC,
CASE WHEN @Ordering = 'desc' THEN C.Month END DESC,
CASE WHEN @Ordering = 'desc' THEN A.StageName END DESC发布于 2016-12-24 11:00:28
虽然我知道您无法摆脱子查询中不同列上的GROUP BY,但您可以使系统变得更容易。
你现在有
S.StageNumber,
S.StageName,
MONTH(TimeIn),
DATENAME(MONTH,TimeIn),
YEAR(TimeIn)我想这是相当多的数据要通过。请允许我作一些猜测:
S.StageNumber, -- int, 4 bytes
S.StageName, -- string, 20 bytes
MONTH(TimeIn), -- int, 4 bytes
DATENAME(MONTH,TimeIn), -- string 5 bytes
YEAR(TimeIn) -- int, 4 byte现在,有一些依赖关系:
这会让我们
S.StageID, -- int, 4 bytes
MONTH(TimeIn), -- int, 4 bytes
YEAR(TimeIn) -- int, 4 byte这意味着GROUP BY的排序只需要每条记录运行12个字节,而不是以前的每条记录37个字节,而且数字排序要比字符串快得多(例如,由于大小写、重音等)。
我尝试相应地重写查询(未经测试!)。我还将月份信息的获取移到了一个单独的临时表中,这将对查询优化器有所帮助。
SELECT DISTINCT Month,MonthName,Year
INTO #dates
FROM Calendar
WHERE DATE >= @StartDate AND DATE < DATEADD (d,1,@EndDate)
CREATE UNIQUE CLUSTERED INDEX uq0_#dates ON #dates (Month,Year)
SELECT
A.StageName,
C.Month,
C.MonthName as Label,
C.Year,
isnull(A.Average,0) as Data
FROM
(
SELECT S.StageName,
MonthNumber,
Year,
Average
FROM (
SELECT VM.StageID,
MONTH(TimeIn) as MonthNumber,
YEAR(TimeIn) as Year,
ISNULL(AVG(DATEDIFF(mi,TimeIn,isnull(TimeOut,@TodayDate))),0) as Average
FROM
VisitMovement VM
WHERE
(VM.TimeIn >= @StartDate AND
VM.TimeIn < DATEADD (d,1,@EndDate)) AND
(VM.TimeOut < DATEADD (d,1,@EndDate) OR VM.TimeOut IS NULL)
GROUP BY
VM.StageID,
MONTH(TimeIn),
YEAR(TimeIn)
) grp
JOIN Stage S
ON S.StageID = grp.StageID
)
) A
RIGHT OUTER JOIN #dates C
on
A.MonthNumber = C.Month and
-- A.Month = C.MonthName and
A.Year = C.Year
ORDER BY
CASE WHEN @Ordering = 'asc' THEN C.Year END ASC,
CASE WHEN @Ordering = 'asc' THEN C.Month END ASC,
CASE WHEN @Ordering = 'asc' THEN A.StageName END ASC,
CASE WHEN @Ordering = 'desc' THEN C.Year END DESC,
CASE WHEN @Ordering = 'desc' THEN C.Month END DESC,
CASE WHEN @Ordering = 'desc' THEN A.StageName END DESC希望这能有所帮助。
发布于 2016-12-19 13:31:14
因为order by必须计算每一行,所以我认为它不能最优地使用索引。将order by替换为默认排序的row_number(),并且只逆转一次顺序,至少可以防止对单个行进行多个@Ordering计算。
在下面的伪代码中,原始查询放在CTE中。Row_number确定升序排序,在cte下面,如果需要,顺序将颠倒:
;with cte as
(
SELECT
A.StageName,
C.Month,
...,
row_number() over (order by C.Year,C.Month,A.StageName) sortOrder
FROM
...rest of the query, excluding the order by
)
select * --or list the columns without the sortOrder
from cte
order by sortOrder * case @Ordering when 'desc' then -1 else 1 end https://stackoverflow.com/questions/41220316
复制相似问题