我有这张桌子:
TopScores
Username char(255)
Score int
DateAdded datetime2
会有很多行。
我针对它运行以下查询(存储过程的代码)以获得前5名高分,并且特定用户名的分数前面是位于他们正上方的人和下面的人:
WITH Rankings
AS (SELECT Row_Number() OVER (ORDER BY Score DESC, DateAdded DESC) AS Pos,
--if score same, latest date higher
Username,
Score
FROM TopScores)
SELECT TOP 5 Pos,
Username,
Score
FROM Rankings
UNION ALL
SELECT Pos,
Username,
Score
FROM Rankings
WHERE Pos BETWEEN (SELECT Pos
FROM Rankings
WHERE Username = @User) - 1 AND (SELECT Pos
FROM Rankings
WHERE Username = @User) + 1
我必须索引表,所以我首先添加了clustered:ci_TopScores(用户名)和非聚集:nci_TopScores(Dateadded,Score)。
查询计划显示群集完全被忽略(在我创建非聚集的I测试之前,它被查询使用),逻辑读取更多(与没有任何索引的表扫描相比)。
排序是成本最高的运营商。所以我将索引调整为聚集:ci_TopScores(Score desc,Dateadded desc)和nonclustered:nci_TopScores(Username)。
仍然排序成本相同。非聚集:nci_TopScores(用户名)再次被完全忽略。
如何有效避免对此表进行排序和索引的高成本?
答案 0 :(得分:0)
CTE不使用用户名,所以不要惊讶它不使用该索引。
CTE只是语法。您正在评估CTE 4次。
尝试#temp,只评估一次。
但你需要考虑指数
我会跳过RowNumber并在#temp上放一个iden pk作为pos
我会跳过#temp
对于TopScores,Score desc,DateAdded desc,Username asc的索引将有助于 但如果它是支离破碎的话,它将无济于事 这是一个在插入
时会分段的索引insert into #temp (Score, DateAdded, Username)
select Score, DateAdded, Username
from TopScores
order by Score desc, DateAdded desc, Username asc
select top 5 *
from #temp
order by pos
union
select three.*
from #temp
join #temp as three
on #temp.UserName = @user
and abs(three.pos - #temp.pos) <= 1
那么如果在#temp UserName上有表扫描怎么办呢 只要创建一个索引,一次扫描就不需要了 无论如何,该指数将严重分裂。