4

在 SQL Server 2012 中使用新的 LAG 和 LEAD 函数有哪些优势?
仅仅是更容易编写和更容易调试查询,还是还有性能改进?

这对我来说很重要,因为我们经常需要这种类型的功能,我需要知道我们是否应该在不久的将来推荐升级。
如果它只是更简单的查询,那么升级的麻烦(和成本)将不值得。

4

2 回答 2

5

为了演示执行计划的不同,我使用了 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 的内部解决方案。或者可能不是。

于 2012-11-02T12:29:53.763 回答
3

我不能对 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 与不执行相同查询之间的比较:

(比较执行计划会很有趣。)

于 2012-10-26T14:44:12.520 回答