按 2 行结果排序会导致 800% 的性能下降

Met*_*hor 1 performance sql-server t-sql sql-server-2012 gaps-and-islands

下面的查询,对一个大约 600 万行的表运行需要 25 秒。当我删除最终订单时,它会在 3 秒内运行。表上没有索引(它是一个中间 SSIS ETL 目标,后来被拉入 DW。)

它不是缓存。我以任何顺序多次运行它,结果一致。

查询本身会检查序列号中的差距。SSIS 将使用它来查看是否需要重新获取任何内容。

;with edges as
(
    select 
        ROW_NUMBER() over (partition by s1.SiteIDNumber order by s1.SequenceNumber) as rn,
        s1.SiteIDNumber, 
        s1.SequenceNumber
    from TestPlay s1
    left join TestPlay s2 on s2.SiteIDNumber = s1.SiteIDNumber and s2.SequenceNumber = s1.SequenceNumber + 1
    left join TestPlay s3 on s3.SiteIDNumber = s1.SiteIDNumber and s3.SequenceNumber = s1.SequenceNumber - 1
    where s2.SiteIDNumber is null 
    or s3.SiteIDNumber is null
),
gaps as
(
    select 
        e.rn,
        e.SiteIDNumber,
        e.SequenceNumber  + 1 as StartSeq,
        e2.SequenceNumber - 1 as EndSeq
    from edges e
    join edges e2 on e2.SiteIDNumber = e.SiteIDNumber and e2.rn = e.rn+1
    where e.rn = (e.rn / 2) * 2
)
select * from gaps order by rn
Run Code Online (Sandbox Code Playgroud)

这是执行计划中唯一似乎不同的部分:

使用 ORDER BY(25 秒):

用 ORDER BY

无订单(3秒):

没有订单

Aar*_*and 6

合并连接就像一个拉链——如果你不关心顺序,SQL Server 知道它可以按照它想要的任何方式对输入进行排序,而不必担心重新排序任何东西。当您添加 order by 时,在这种情况下合并连接不再是最佳选择,因为按照表达式定义的顺序对第一个 CTE 进行两次物化和排序显然必须比看起来更昂贵。ROW_NUMBER()

一种解决方法,在周五的啤酒时间想一下袖口:

;WITH ...
(
)
SELECT * INTO #x FROM gaps;

SELECT * FROM #x ORDER BY rn;
Run Code Online (Sandbox Code Playgroud)

但是,我敢打赌,如果您四处寻找一个名叫 Itzik Ben-Gan 的人,您会发现更有效的方法来解决涉及间隙和岛屿的问题(或者建议不要尝试以您正在排序的方式进行排序)。