根据上一行的值更新值

时间:2021-04-13 22:25:44

标签: sql-server tsql recursion sql-update

我有下表:

CREATE TABLE dbo.Persons 
(
     [Date] date null, 
     [PersonId] int null, 
     [Amount] int null, 
     [Value] int null
)

这是一些示例数据:

INSERT INTO dbo.Persons ([Date], [PersonId], [Amount], [Value])
VALUES
('2020-01-01', 1, 200, NULL),
('2020-01-02', 1, 300, NULL),
('2020-01-03', 1, 400, NULL),
('2020-01-04', 1, 500, NULL),
('2020-01-01', 2, 200, NULL),
('2020-01-02', 2, 300, NULL),
('2020-01-03', 2, 400, NULL),
('2020-01-04', 2, 500, NULL),
('2020-01-01', 3, 0, NULL),
('2020-01-02', 3, 0, NULL),
('2020-01-03', 3, 0, NULL),
('2020-01-04', 3, 0, NULL)

我的目标:根据其他列中的值和 [Value] 列中的先前值更新 [Value] 列中的所有值。主键是 date+personId

用伪代码解释,我的逻辑需要类似于:

CASE 
    WHEN [Amount] > 200 AND previous row value IN [Value] = 1, then 2
    WHEN [Amount] > 500 AND previous row value in [Value] = 2, then 3
    WHEN [Date] > '2020-01-01' AND [Amount] = 500 AND previous row value in [Value] = 2, then 4

等等 - 这捕获了一般的 T-SQL 代码逻辑..

我只想更新 [Value] 列,其中 [Value] 也为 NULL。

这里有什么戏?我已经阅读过有关使用 LAG、While LOOP、递归 CTE 等的信息,但不确定该去哪里。

2 个答案:

答案 0 :(得分:0)

您可以使用 lag 来引用前一行的值。在您的示例中,数据 value 将保留为 null,因为您的 case 逻辑取决于从 null 开始的前一行。

但是,这就是你可能的处理方式

with u as (
    select [Date], PersonId,
    case
        when Amount > 200 and Lag(Value) over(partition by PersonId order by [Date]) = 1 then 2
        when Amount > 500 and Lag(Value) over(partition by PersonId order by [Date]) = 2 then 3
        when [Date] > '20200101' and Amount = 500 and Lag(Value) over(partition by PersonId order by [Date])= 2 then 4
    end [Value]
    from Persons
)
update p set
    p.Value=u.Value
from u join Persons p on p.[Date]=u.[Date] and p.PersonId=u.PersonId

答案 1 :(得分:0)

您可以添加行号,然后自连接。您可以将此想法扩展到 Row-2、Row-3 等。

With Qry1 (
    Select  -- Columns from Person table
            , ROWNUMBER() OVER(PARTITION BY PersonId ORDER BY Date) As Seq
    FROM Persons
)
Select  -- Columns from ThisRow
        -- Columns from PrevRow (values will be null if there is
        -- no previous row for this PersonId and Date)
FROM Qry1 ThisRow
LEFT JOIN Qry1 PrevRow
ON ThisRow.PersonId = PrevRow.PersonId
AND ThisRow.Date = PrevRow.Date
AND ThisRow.Seq - 1 = PrevRow.Seq