使用Join和Window函数获取超前和滞后值的性能比较

Jas*_*son 14 performance join sql-server window-functions sql-server-2016 query-performance

我有20M行的表,每一行有3列:timeid,和value。对于每个idtimevalue状态都有一个。我想知道time特定id.

我使用了两种方法来实现这一点。一种方法是使用加入和另一种方法是使用功能导致的窗口/滞后与聚簇索引timeid

我通过执行时间比较了这两种方法的性能。join方法需要16.3秒,窗口函数方法需要20秒,不包括创建索引的时间。这让我感到惊讶,因为窗口函数似乎是先进的,而连接方法是蛮力的。

下面是这两种方法的代码:

创建索引

create clustered index id_time
 on tab1 (id,time)
Run Code Online (Sandbox Code Playgroud)

加入方式

select a1.id,a1.time
   a1.value as value, 
   b1.value as value_lag,
   c1.value as value_lead
into tab2
from tab1 a1
left join tab1 b1
on a1.id = b1.id
and a1.time-1= b1.time
left join tab1 c1
on a1.id = c1.id
and a1.time+1 = c1.time
Run Code Online (Sandbox Code Playgroud)

使用SET STATISTICS TIME, IO ON以下方法生成的 IO 统计信息:

连接方法的统计

这是join方法的执行计划

窗函数法

select id, time, value, 
   lag(value,1) over(partition by id order by id,time) as value_lag,
   lead(value,1) over(partition by id order by id,time) as value_lead
into tab2
from tab1
Run Code Online (Sandbox Code Playgroud)

(仅通过订购可time节省 0.5 秒。)

这是Window函数方法的执行计划

IO统计

[窗函数方法4的统计]


我检查了数据,sample_orig_month_1999似乎原始数据按id和排序得很好time。这是性能差异的原因吗?

看起来join方法比窗口函数方法有更多的逻辑读取,而前者的执行时间实际上更少。是不是因为前者的并行性更好?

我喜欢窗口函数方法,因为代码简洁,对于这个特定的问题,有什么方法可以加快速度吗?

我在 Windows 10 64 位上使用 SQL Server 2016。

Pau*_*ite 13

与自联接相比,LEADLAG窗口函数的行模式性能相对较低并不是什么新鲜事。例如,Michael Zilberstein 早在 2012 年在 SQLblog.com 上写过它。(重复的)Segment、Sequence Project、Window Spool 和 Stream Aggregate 计划运算符有相当多的开销:

计划部分

在 SQL Server 2016 中,您有一个新选项,即为窗口聚合启用批处理模式。这需要表上的某种列存储索引,即使它是空的。优化器当前需要列存储索引才能考虑批处理模式计划。特别是,它启用了更高效的 Window Aggregate 批处理模式运算符。

要在您的情况下对此进行测试,请创建一个空的非聚集列存储索引:

 -- Empty CS index
CREATE NONCLUSTERED COLUMNSTORE INDEX dummy 
ON dbo.tab1 (id, [time], [value]) 
WHERE id < 0 AND id > 0;
Run Code Online (Sandbox Code Playgroud)

查询:

SELECT
    T1.id,
    T1.[time],
    T1.[value],
    value_lag = 
        LAG(T1.[value]) OVER (
            PARTITION BY T1.id
            ORDER BY T1.[time]),
    value_lead =
        LEAD(T1.[value]) OVER (
            PARTITION BY T1.id
            ORDER BY T1.[time])
FROM dbo.tab1 AS T1;
Run Code Online (Sandbox Code Playgroud)

现在应该给出一个执行计划,如:

批处理模式行存储计划

...这可能会执行得更快。

OPTION (MAXDOP 1)将结果存储在新表中时,您可能需要使用或其他提示来获得相同的计划形状。该计划的并行版本需要批处理模式排序(或可能是两个),这可能会慢一点。这更取决于您的硬件。

有关批处理模式窗口聚合运算符的更多信息,请参阅 Itzik Ben-Gan 的以下文章: