LAG 和 LEAD 函数
Posted
技术标签:
【中文标题】LAG 和 LEAD 函数【英文标题】:LAG and LEAD functions 【发布时间】:2012-10-18 11:09:15 【问题描述】:在 SQL Server 2012 中使用新的 有哪些优势? 仅仅是更容易编写和更容易调试查询,还是还有性能改进?
这对我来说很重要,因为我们经常需要这种类型的功能,我需要知道我们是否应该在不久的将来推荐升级。 如果只是更简单的查询,那么升级的麻烦(和成本)就不值得了。
【问题讨论】:
你能澄清一下这个问题吗?您说您已经需要这种类型的功能,但您想知道是否需要这些功能。据推测,这意味着您正在使用替代解决方案来解决您的需求,在这种情况下,它们是什么? 我使用 LAG 重写了一个自连接查询(参见 my question),除了更小更简单之外,查询时间从 2.6 秒下降到 1 秒。 (或者如果您计算查询优化器的不良行为,则从 40 秒到 1 秒)。显然这只是一则轶事,但性能差异令我震惊且极具说服力。 【参考方案1】:为了展示执行计划的不同,我使用了 Dave 的 SQL 权威博客中的获胜解决方案:
Solution to Puzzle – Simulate LEAD() and LAG() without Using SQL Server 2012 Analytic Function;WITH T1
AS (SELECT row_number() OVER (ORDER BY SalesOrderDetailID) N
, s.SalesOrderID
, s.SalesOrderDetailID
FROM
TempDB.dbo.LAG s
WHERE
SalesOrderID IN (20120303, 20120515, 20120824, 20121031))
SELECT SalesOrderID
, SalesOrderDetailID AS CurrentSalesOrderDetailID
/* , CASE
WHEN N % 2 = 1 THEN
max(CASE
WHEN N % 2 = 0 THEN
SalesOrderDetailID
END) OVER (PARTITION BY (N + 1) / 2)
ELSE
max(CASE
WHEN N % 2 = 1 THEN
SalesOrderDetailID
END) OVER (PARTITION BY N / 2)
END LeadVal */
, CASE
WHEN N % 2 = 1 THEN
max(CASE
WHEN N % 2 = 0 THEN
SalesOrderDetailID
END) OVER (PARTITION BY N / 2)
ELSE
max(CASE
WHEN N % 2 = 1 THEN
SalesOrderDetailID
END) OVER (PARTITION BY (N + 1) / 2)
END PreviousSalesOrderDetailID
FROM
T1
ORDER BY
SalesOrderID
, SalesOrderDetailID;
SELECT SalesOrderID
, SalesOrderDetailID AS CurrentSalesOrderDetailID
, LAG(SalesOrderDetailID, 1, 0) OVER (ORDER BY SalesOrderID, SalesOrderDetailID) AS PreviousSalesOrderDetailID
FROM TempDB.dbo.LAG
WHERE SalesOrderID IN (20120303, 20120515, 20120824, 20121031);
Warning: Null value is eliminated by an aggregate or other SET operation.
(10204 row(s) affected)
Table 'Worktable'. Scan count 6, logical reads 81638, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'LAG'. Scan count 4, logical reads 48, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times: CPU time = 297 ms, elapsed time = 332 ms.
--- versus ---
(10204 row(s) affected)
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'LAG'. Scan count 4, logical reads 48, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times: CPU time = 78 ms, elapsed time = 113 ms.
除了更优雅之外,它消耗的资源也更少。
以下是图形执行计划的比较:
在这种特定情况下,执行计划显示出明显的赢家。 Dave 的页面有许多可能的不同方式来获得 LEAD/LAG 功能。也许其中一些会击败 SQL Server 的内部解决方案。或者,也许不是。
【讨论】:
【参考方案2】:我不能对 MS SQL Server 2012 发表太多评论,但从 PostgreSQL 的角度来看,这些功能自版本 8.4 起就已可用。
一般来说,它们非常便于检测变化(通常在时间序列中,与ORDER BY
结合使用)。通常:
WITH shifted_timeseries AS (
SELECT event_time,
value,
LAG(value) OVER (ORDER BY event_time) AS lagged_value
FROM timeseries
)
SELECT event_time AS change_time, value AS new_value
FROM shifted_timeseries
WHERE value != lagged_value;
对于这类事情,仅就清晰度而言,它们是值得的(尽管这可能是主观的)。
对于更复杂的操作,例如,如果你想要连续值的时间段,this answer 是解决这个问题的一个非常优雅的解决方案。根据this SQLFiddle,它似乎在 SQL Server 2012 中运行良好。
这两个博客条目还显示了使用 LEAD/LAG 与不使用相同查询的比较:
Write T-SQL Self Join Without Using LEAD and LAG Solution to Puzzle – Simulate LEAD() and LAG() without Using SQL Server 2012 Analytic Function(比较执行计划会很有趣。)
【讨论】:
以上是关于LAG 和 LEAD 函数的主要内容,如果未能解决你的问题,请参考以下文章