使用现有标志列捕获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