使用现有标志列捕获SQL行更改(多列)

使用现有标志列捕获SQL行更改(多列),sql,sql-server,sql-server-2005,Sql,Sql Server,Sql Server 2005,我的车辆登记表如下: CREATE TABLE [registrations] ( [VIN] int, [Name] nvarchar(255), [Action] nvarchar(255), [Date] DateTime ); INSERT INTO registrations VALUES (1, 'John', 'Add', '2017-01-01'), (1, '', 'Remove', '2017-01-16'), (1, 'Fred', 'A

我的车辆登记表如下:

CREATE TABLE [registrations]
(
    [VIN] int,
    [Name] nvarchar(255),
    [Action] nvarchar(255),
    [Date] DateTime
);

INSERT INTO registrations VALUES 
(1, 'John', 'Add', '2017-01-01'),
(1, '', 'Remove', '2017-01-16'),
(1, 'Fred', 'Add', '2017-02-25'),
(1, 'Tom', 'Change', '2017-06-08'),
(2, 'Nancy', 'Add', '2018-01-15'),
(2, 'Jim', 'Change', '2018-02-05'),
(3, 'Clarence', 'Add', '2018-02-10'),
(3, 'Darlene', 'Change', '2018-02-11'),
(4, 'Charlotte', 'Add', '2018-02-11'),
(5, 'Ferris', 'Add', '2018-02-12'),
(5, 'Dante', 'Change', '2018-02-12'),
(5, 'Susan', 'Change', '2018-02-13');
我试图只捕获操作
更改
,但我希望更改前后的
名称
值作为同一行中的不同列

因此,对上述内容的查询将返回如下结果:

VIN    OldName    NewName    Date
1      Fred       Tom        2017-02-25
2      Nancy      Jim        2018-02-05
3      Clarence   Darlene    2018-02-11
5      Dante      Susan      2018-02-13
注意:我排除了只有一个事务的VIN,并且
更改
可以来自多个状态(例如
添加

其他答案()显示了一种很好的方法,但是通过监视单个列的变化。我有多个列正在更改(
Name
只是其中一个示例),其中有一个标志列(
Action
)指示对任何行进行了更改

我可以使用另一个答案()的指导将其转换为日志格式:

但就透视而言,我不确定如何考虑每个
VIN可能有2个或10个条目这一事实。我只想要最近的两个(最近的是
更改

我可以在应用程序级别对上面的查询进行额外的处理,但我想知道如何在SQL中进行处理

RDBMS:MS SQL Server 2005


SQLFiddle

在SQLServer2012中,使用
LAG()
这要容易得多。在SQL Server 2005中,可以使用相关子查询或
APPLY

SELECT r.VIN, r.Name as OldName, rprev.Name as NewName, r.Date
FROM registrations r OUTER APPLY
     (SELECT TOP (1) r2.*
      FROM registrations r2
      WHERE r2.VIN = r.VIN AND r2.[Date] < r.[Date]
      ORDER BY r2.[Date] DESC
     ) rprev
WHERE r.action = 'Change'
ORDER BY r.[VIN], r.[Date] DESC;
是此版本的SQL FIDLE

VIN |旧名|新名|日期 --: | :------- | :------ | :------------------ 1 |弗雷德|汤姆| 2017年6月8日00:00:00 2 |南希|吉姆| 2018年2月5日00:00:00 3 |克拉伦斯|达琳| 2018年2月11日00:00:00 5 |但丁|苏珊| 2018年2月13日00:00:00
b小提琴

哇,谢谢你。当状态从一个更改变为另一个更改时,您如何考虑只需要最新的更改?(例如,只看到从但丁到苏珊的变化,而不看到费里斯到但丁的变化)@JayM。公平的问题。这似乎是你在最初的问题中想要的。原来我错过了那部分。太好了,谢谢你的帮助。另外,感谢您让我了解
Apply
。这改变了游戏规则
SELECT r.VIN, r.Name as OldName, rprev.Name as NewName, r.Date
FROM registrations r OUTER APPLY
     (SELECT TOP (1) r2.*
      FROM registrations r2
      WHERE r2.VIN = r.VIN AND r2.[Date] < r.[Date]
      ORDER BY r2.[Date] DESC
     ) rprev
WHERE r.action = 'Change'
ORDER BY r.[VIN], r.[Date] DESC;
SELECT r.*, rprev.Name
FROM (SELECT r.*,
             ROW_NUMBER() OVER (PARTITION BY VIN ORDER BY [Date] DESC) as seqnum
      FROM registrations r
      WHERE r.action = 'Change'
     ) r OUTER APPLY
     (SELECT TOP (1) r2.*
      FROM registrations r2
      WHERE r2.VIN = r.VIN AND r2.[Date] < r.[Date]
      ORDER BY r2.[Date] DESC
     ) rprev
WHERE seqnum = 1;
with x as
(
    select VIN, Name, Action, [Date],
           row_number() over (partition by VIN order by VIN, [date]) rn,
           count(*) over (partition by VIN) cnt
    from   registrations
)
select x.VIN, 
       (select x1.Name
        from   x x1
        where  x1.VIN = x.VIN
        and    x1.rn = x.rn - 1) as OldName,
       x.Name as NewName, 
       [Date]
from   x
where  x.[Action] = 'Change'
and    x.cnt > 1
and    x.rn = (select top(1) x2.rn from x x2 where x2.VIN = x.Vin order by x2.VIN, x2.rn desc)

GO
VIN | OldName | NewName | Date --: | :------- | :------ | :------------------ 1 | Fred | Tom | 08/06/2017 00:00:00 2 | Nancy | Jim | 05/02/2018 00:00:00 3 | Clarence | Darlene | 11/02/2018 00:00:00 5 | Dante | Susan | 13/02/2018 00:00:00