SQL中的递归滞后列计算

时间:2015-06-22 18:09:53

标签: sql sql-server recursion

我正在尝试编写一个将计算的表数据插入另一个表的过程。

我遇到的问题是我需要每行的计算列受上一行计算列结果的影响。我试图延迟计算本身,但这不起作用!

如:

(Max是我创建的函数,返回两个值中的最高值)

Id   Product      Model      Column1    Column2
1    A            1          5          =MAX(Column1*2, Lag(Column2))
2    A            2          2          =MAX(Column1*2, Lag(Column2))
3    B            1          3          =MAX(Column1*2, Lag(Column2))

如果我在SQL中尝试以上操作:

SELECT
    Column1, 
    MyMAX(Column1,LAG(Column2, 1, 0) OVER (PARTITION BY Product ORDER BY Model ASC) As Column2 
FROM Source

......它说第2列未知。

如果我LAG进行Column2计算,我得到输出:

Select Column1, MyMAX(Column1,LAG(Column1*2, 1, 0) OVER (PARTITION BY Product ORDER BY Model ASC) As Column2

Id   Column1    Column2
1    5          10
2    2          10
3    3          6

第3行为什么6?因为3 * 2> 2 * 2。

我想要的输出:

Id   Column1    Column2
1    5          10
2    2          10
3    3          10

第3行为什么10?因为先前的结果10> 3 * 2

问题是我不能滞后Column2的结果 - 我只能滞后于其他列或计算它们!

是否有使用LAG实现此目的的技术,还是我必须使用递归CTE?我读到LAG接替CTE所以我认为它是可能的。如果没有,这个'CTE'会是什么样的?

编辑:或者 - 我还能做些什么来解决这个问题?

1 个答案:

答案 0 :(得分:5)

修改

事后看来,这个问题是在Column1 * 2之上运行的分区最大值。它可以像

一样简单地完成
SELECT Id, Column1, Model, Product,
       MAX(Column1 * 2) OVER (Partition BY Model, Product Order BY ID ASC) AS Column2
FROM Table1;

Fiddle

原始答案

通过加入递增的行号,这里有一种方法可以通过递归CTE完成此操作,而不需要LAG。我没假设您的Id是连续的,因此添加了额外的ROW_NUMBER()。您没有提到任何分区,因此没有应用相同的分区。查询只是从第一行开始,然后投影当前Column1 * 2或前面的Column2

中的较大者
WITH IncrementingRowNums AS
(
    SELECT Id, Column1, Column1 * 2 AS Column2, 
           ROW_NUMBER() OVER (Order BY ID ASC) AS RowNum
    FROM Table1
),
lagged AS
(
    SELECT Id, Column1, Column2, RowNum
    FROM IncrementingRowNums
    WHERE RowNum = 1

    UNION ALL

    SELECT i.Id, i.Column1, 
        CASE WHEN (i.Column2 > l.Column2) 
            THEN i.Column2 
            ELSE l.Column2 
        END, 
        i.RowNum
    FROM IncrementingRowNums i
    INNER JOIN lagged l
    ON i.RowNum = l.RowNum + 1
)
SELECT Id, Column1, Column2
FROM lagged;

SqlFiddle here

修改,重新分区

分区大致相同,只需拖动Model + Product列,然后在行编号中进行分区(即每次产品或模型重置时从1开始),包括CTE JOIN条件中的这些和也在最后的订购中。

WITH IncrementingRowNums AS
(
    SELECT Id, Column1, Column1 * 2 AS Column2, Model, Product,
           ROW_NUMBER() OVER (Partition BY Model, Product Order BY ID ASC) AS RowNum
    FROM Table1
),
lagged AS
(
    SELECT Id, Column1, Column2, Model, Product, RowNum
    FROM IncrementingRowNums
    WHERE RowNum = 1

    UNION ALL

    SELECT i.Id, i.Column1, 
        CASE WHEN (i.Column2 > l.Column2) 
            THEN i.Column2 
            ELSE l.Column2 
        END, 
        i.Model, i.Product,
        i.RowNum
    FROM IncrementingRowNums i
    INNER JOIN lagged l
    ON i.RowNum = l.RowNum + 1 
    AND i.Model = l.Model AND i.Product = l.Product
)
SELECT Id, Column1, Column2, Model, Product
FROM lagged
ORDER BY Model, Product, Id;

Updated Fiddle