【问题标题】:Recursively retrieve LAG() value of previous record递归检索上一条记录的 LAG() 值
【发布时间】:2017-07-10 16:02:33
【问题描述】:

我进行了以下涉及 LAG() 的计算:

(lag(fValue,1,fValue) OVER (PARTITION BY Cluster ORDER BY iSequence) + fValue) / 2 as fValueAjusted

它取上一个(基于 iSequence)记录的 fValue,与当前记录相加,然后除以 2。

但是,我必须使用先前记录的 fValueAjusted,而不是使用 fValue。

这意味着第一条记录的 fValueAjusted 将是它自己的 fValue。第二条记录的 fValueAjusted 将基于第一条记录的 fValue。并且,从第三条记录开始,将根据前一条记录的fValueAjusted计算。

我需要在上一条记录的 fValueAjusted 上递归计算 fValueAjusted。我不知道该怎么做。

更新 1:这是源数据的示例。真实表有数百条记录和80个簇。

CREATE TABLE dbo.example (
    iUnity      int NOT NULL,
    Cluster     char(2) NOT NULL,
    fValue      float NOT NULL
)

15  A1      150
17  A1      170
21  B2      210
23  B2      230
71  C3      710

这是计算序列的 CTE:

WITH cteSequencing AS (
    SELECT
        iUnity,Cluster
        ,fValue as fValueOriginal
        ,row_number() OVER (PARTITION BY Cluster ORDER BY fValueOriginal) as iSequence
    FROM dbo.example
)

如果 fValueAjusted 将基于 fValueOriginal 计算,查询将如下所示:

SELECT
    iUnity,Cluster,fValueOriginal
    ,(
        lag(fValue,1,fValue) OVER (PARTITION BY Cluster ORDER BY iSequence)
        + fValueOriginal
    ) / 2 as fValueAjusted
FROM cteSequencing

但一条记录的 fValueAjusted 必须根据前一条记录的 fValueAjusted 计算。它会是这样的:

SELECT
    iUnity,Cluster,fValueOriginal
    ,(
        lag(fValueAjusted,1,fValueOriginal) OVER (PARTITION BY Cluster ORDER BY iSequence)
        + fValueOriginal
    ) / 2 as fValueAjusted
FROM cteSequencing

当然 fValueAjusted 在执行时不可用。 LAG() 必须递归地计算一条记录的列,然后提供该列供下一条记录使用。

【问题讨论】:

  • 样本数据和预期输出将在这里有所帮助。
  • 创建一个附加列(或临时表),插入值,然后将其用于计算。使用提供的有限信息,我能提供的最佳建议。
  • 我不能使用临时表,因为计算是递归的。 “函数”的输入是“函数”本身的输出。如果我可以使用函数来做到这一点,它就可以做到,但我不知道怎么做。
  • 我将尝试提供一些示例。

标签: sql-server recursion sql-server-2012 lag


【解决方案1】:

更新:原始答案不正确

这是正确的:

代码使用递归 CTE

CREATE TABLE #example (
    iUnity      int NOT NULL,
    Cluster     char(2) NOT NULL,
    fValue      float NOT NULL
)
INSERT INTO #example
VALUES
( 15,  'A1',      150 ),
( 16,  'A1',      170 ),
( 17,  'A1',      190 ),
( 18,  'A1',      210 ),
( 21,  'B2',      210 ),
( 23,  'B2',      230 ),
( 71,  'C3',      710 )

WITH cteSequencing AS (
    -- Get Values Order
    SELECT iUnity, Cluster, fValue, fValue AS fValueAjusted,
        ROW_NUMBER() OVER (PARTITION BY Cluster ORDER BY fValue) AS iSequence
    FROM #example
),
Recursion AS(
    -- Anchor - the first value in clusters
    SELECT iUnity, Cluster, fValue, fValueAjusted, iSequence
    FROM cteSequencing
    WHERE iSequence = 1
    UNION ALL
    -- Calculate next value based on the previous
    SELECT b.iUnity As iUnity, b.Cluster, b.fValue,
        ( a.fValueAjusted + b.fValue ) / 2 AS fValueAjusted,
        b.iSequence
    FROM Recursion AS a
        INNER JOIN cteSequencing AS b ON a.iSequence + 1 = b.iSequence AND a.Cluster = b.Cluster
)
SELECT * FROM Recursion ORDER BY Cluster, fValue

-- Manually check results
SELECT ( 150 + 170 ) / 2
SELECT ( 190 + 160 ) / 2 
SELECT ( 190 + 170 ) / 2

输出:

iUnity      Cluster fValue                 fValueAjusted          iSequence
----------- ------- ---------------------- ---------------------- --------------------
15          A1      150                    150                    1
16          A1      170                    160                    2
17          A1      190                    175                    3
18          A1      210                    192.5                  4
21          B2      210                    210                    1
23          B2      230                    220                    2
71          C3      710                    710                    1

更新

如果遇到

最大递归100已经用完之前的语句 完成

错误,然后使用OPTION (MAXRECURSION xxx) 设置更大的递归限制,最高可达 32,767。

【讨论】:

  • 我在同一条船上,虽然使用的是 Presto,但我担心它也需要递归;不幸的是,我目前无法使用。我想没有其他方法可以解决它,不需要递归......?
  • @Jonny - 对于这个特定问题,您需要递归或能力来存储中间计算结果。 AFAIK(在 SQL Server 中)没有递归,您需要使用循环/游标循环。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-02
  • 1970-01-01
相关资源
最近更新 更多