使用历史行值从表构建更改历史

时间:2020-07-15 05:18:24

标签: sql sql-server tsql

我有一个表,该表存储其他表中数据的历史值:

ObjectId | Value | UpdatedAt
1        |     A | 2020-07-15
1        |     B | 2020-07-16
1        |     C | 2020-07-17
2        |     A | 2020-07-15
2        |     B | 2020-07-16

现在,我需要从此类表中生成“更改日志”,该表将显示旧值,新值以及更新发生的时间:

ObjectId | OldValue | NewValue | UpdatedAt
1        |     A    |        B | 2020-07-16
1        |     B    |        C | 2020-07-17
2        |     A    |        B | 2020-07-16

不幸的是,我无法更改现有表的结构,也无法在其中放置旧值,我需要一个查询来提取它。

4 个答案:

答案 0 :(得分:2)

您可以使用窗口功能lead()ObjectId进行分区。这是demo

select
  ObjectId,
  Value,
  NewValue,
  UpdatedAt
from
(
  select
    ObjectId,
    Value,
    lead(value) over (partition by ObjectId order by UpdatedAt) as NewValue,
    lead(UpdatedAt) over (partition by ObjectId order by UpdatedAt) as UpdatedAt
  from Table1
) subq
where NewValue is not null
order by ObjectId

输出:

| ObjectId  Value NewValue  UpdatedAt |
*-------------------------------------*
|  1        1       2      2020-07-16 |
|  1        2       3      2020-07-17 |
|  2        1       2      2020-07-16 |

答案 1 :(得分:1)

您可以使用窗口功能来获得结果。

DECLARE @historyTable table(ObjectId int, Value int, UpdatedAt date)

insert into @historyTable values
(1        ,     1 ,'2020-07-15'),
(1        ,     2 ,'2020-07-16'),
(1        ,     3 ,'2020-07-17'),
(2        ,     1 ,'2020-07-15'),
(2        ,     2 ,'2020-07-16');

SELECT * from
(
SELECT objectid, value as oldvalue,
lead(value,1) over (partition by objectid order by updatedat) as newvalue,
lead(UpdatedAt,1) over (partition by objectid order by updatedat) as updatedat
FROM @historyTable
) as t
where t.updatedat is not null


+----------+----------+----------+------------+
| objectid | oldvalue | newvalue | updatedat  |
+----------+----------+----------+------------+
|        1 |        1 |        2 | 2020-07-16 |
|        1 |        2 |        3 | 2020-07-17 |
|        2 |        1 |        2 | 2020-07-16 |
+----------+----------+----------+------------+


答案 2 :(得分:0)

with cte as (
     select row_number() over (partition by objectid order by UpdatedAt ) rnk,[ObjectId], [Value], [UpdatedAt]
     from T
)
select t1.ObjectId,t2.Value as OldValue ,t1.Value as NewValue ,t1.UpdatedAt
from (
    select * from cte 
    where rnk <> 1
) t1
left join cte t2 on t1.objectid =t2.objectid and t1.rnk -1 = t2.rnk

表脚本:

CREATE TABLE T
    ([ObjectId] int, [Value] int, [UpdatedAt] datetime)
;
    
INSERT INTO T
    ([ObjectId], [Value], [UpdatedAt])
VALUES
    (1, 1, '2020-07-15 00:00:00'),
    (1, 2, '2020-07-16 00:00:00'),
    (1, 3, '2020-07-17 00:00:00'),
    (2, 1, '2020-07-15 00:00:00'),
    (2, 2, '2020-07-16 00:00:00')
;

demo link

image

答案 3 :(得分:0)

    with changeLogTemp as
    (
    select ROW_NUMBER() over (order by ObjectId,UpdatedAt ) row_number ,* from changeLog
    )
    
    select 
    l.ObjectId,
    l.Value OldValue,
    r.Value NewValue,
    r.UpdatedAt 
    
    from changeLogTemp l 
    left join changeLogTemp r on l.ObjectId =r.ObjectId and l.row_number =r.row_number-1
    where r.UpdatedAt is not null

您也可以使用Widowfunction (Lead())解决这种情况

但是建议的解决方案非常简单 同时,对于行与两列或更多列之间的类似操作(computational and comparative),它非常有用