在 SQL Server 2012 中使用新的 LAG 和 LEAD 函数有哪些优势?
仅仅是更容易编写和更容易调试查询,还是还有性能改进?
这对我来说很重要,因为我们经常需要这种类型的功能,我需要知道我们是否应该在不久的将来推荐升级。
如果它只是更简单的查询,那么升级的麻烦(和成本)将不值得。
在 SQL Server 2012 中使用新的 LAG 和 LEAD 函数有哪些优势?
仅仅是更容易编写和更容易调试查询,还是还有性能改进?
这对我来说很重要,因为我们经常需要这种类型的功能,我需要知道我们是否应该在不久的将来推荐升级。
如果它只是更简单的查询,那么升级的麻烦(和成本)将不值得。
为了演示执行计划的不同,我使用了 Dave 的 SQL Authority 博客中的获胜解决方案:
;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 的内部解决方案。或者可能不是。
我不能对 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;
对于这类事情,仅就清晰度而言,它们是值得的(尽管这可能是主观的)。
对于更复杂的操作,例如,如果您想要连续值的时间段,则此答案是解决此问题的一个非常优雅的解决方案。根据这个 SQLFiddle ,它似乎在 SQL Server 2012 中工作得很好。
这两个博客条目还显示了使用 LEAD/LAG 与不执行相同查询之间的比较:
(比较执行计划会很有趣。)