无法获取存储过程以将索引扫描更改为索引查找

Can't get stored procedure to change index scan to index seek

背景:我 运行正在 Azure 上的 Sql Server 2014 (12.0.2000.8) 上...

前几天我发现了一个方便的脚本,它显示了作为 "touching" 索引的 queries/stored 过程。我一直在寻找这个,因为我有一些性能很差的索引,但我找不到它们被调用的地方。

现在我有了这些信息,我一直在尝试 re-work 触及相关索引的过程。

在查看我的查询的执行计划时,它说它正在进行扫描,这显然不是最佳的。

将鼠标悬停在索引上会显示连接的输出列表,但没有谓词。

我继续使用该输出列表中的确切字段创建了一个索引。

这是正在 运行 的查询:

declare @season int = 2017

select  s.SchoolId,
        s.Name [SchoolName],
        s.Conference,
        tr.DualRank [Rank],
        convert(varchar(2), tr.DualWins) + ' - ' + convert(varchar(2), tr.DualLosses) [Record],
        tr.RankingDate,
        case    when tr.WeekNumber = 0 then null
                else
                    (select trx.DualRank from dbo.TeamRankings trx where trx.Season = tr.Season and trx.WeekNumber = (tr.WeekNumber - 1) and trx.SchoolId = tr.SchoolId)
                    - tr.DualRank
        end [Trend],
        (select trx.DualRank from dbo.TeamRankings trx where trx.Season = tr.Season and trx.WeekNumber = (tr.WeekNumber - 1) and trx.SchoolId = tr.SchoolId) [PreviousWeek]
from    dbo.TeamRankings tr
join    dbo.School s on s.SchoolId = tr.SchoolId
where   tr.Season = @season
and     tr.IsCurrent = 1
order by tr.DualRank

此列表中唯一具有扫描而不是搜索的连接是学校 table。它在 SchoolId 上加入,然后在 select 部分,它输出名称和会议。看起来很简单。

在我的第一次尝试中,我继续创建了这样的索引:

create nonclustered index idx_NC_School_SchoolId_incs on dbo.School (SchoolId asc) include (Name, Conference)

但这仍然会导致扫描。我的第二次尝试是这样做的:

create nonclustered index idx_NC_School_SchoolId_Name_Conference on dbo.School (SchoolId asc, Name asc, Conference asc)

但是它仍然在使用我创建的索引进行扫描。

我还应该注意什么才能尝试让此查询执行查找而不是扫描。

有关更多背景信息,这里是 table 定义的一个子集:

dbo.School
SchoolId int identity(1,1) primary key,
Name varchar(100) not null,
Conference varchar(100) not null -- will soon change this to a lookup table
......

我知道有人会问,但我不知道该怎么做;如何将我的执行计划附加到问题中?

这里是 link 显示数据的页面:http://www.wrestlestat.com/rankings/dual/live

索引扫描并不总是坏事,特别是当你有一个非常小的 table 时。

但是绝对可以提高查询性能的方法是将 sub-queriesselect 子句移至 from 并使用 join

有点像……

declare @season int = 2017

select  s.SchoolId,
        s.Name [SchoolName],
        s.Conference,
        tr.DualRank [Rank],
        convert(varchar(2), tr.DualWins) + ' - ' + convert(varchar(2), tr.DualLosses) [Record],
        tr.RankingDate,
        CASE WHEN tr.WeekNumber =  0 then null
             ELSE trx.DualRank - tr.DualRank end [Trend],
        trx.DualRank  [PreviousWeek]
from    dbo.TeamRankings tr
Inner join dbo.School       s   on s.SchoolId = tr.SchoolId

Left  join dbo.TeamRankings trx ON trx.Season = tr.Season 
                               and trx.WeekNumber = (tr.WeekNumber - 1) 
                               and trx.SchoolId = tr.SchoolId
where   tr.Season = @season
and     tr.IsCurrent = 1
order by tr.DualRank

select 子句中有 sub-query 时,sub-query 会针对 outer query 返回的每一行执行,如果将其移动到 from 子句并使用连接,它将是 executed once 并且结果集将与来自其他连接的结果集连接。更高效,更清洁。

您可以使用 LAG 和 LEAD 等窗口函数绕过 table 的自连接。 它可以导致更简单的执行计划。

declare @season int = 2017

select  
    s.SchoolId,
    s.Name [SchoolName],
    s.Conference,
    tr.DualRank [Rank],
    convert(varchar(2), tr.DualWins) + ' - ' + convert(varchar(2), tr.DualLosses) [Record],
    tr.RankingDate,
    CASE WHEN tr.WeekNumber = 0 THEN NULL ELSE tr.DualRank - LAG(tr.DualRank,1,0) OVER(Partition BY tr.Season,tr.SchoolId ORDER BY trx.WeekNumber) END AS [Trend],
    LAG(tr.DualRank,1,0) OVER(Partition BY tr.Season,tr.SchoolId ORDER BY trx.WeekNumber) AS  [PreviousWeek]

from    
    dbo.TeamRankings tr
        join    dbo.School s on s.SchoolId = tr.SchoolId
where   
    tr.Season = @season
    and     
    tr.IsCurrent = 1
order by 
    tr.DualRank

当你使用

trx.WeekNumber = (tr.WeekNumber - 1) 

您正在更改 tr.WeekNumber 的值,因此它与存储在索引中的值不同,因此 SQL 将执行扫描而不是查找。