It's possible to see how the data in the table looked at a previous moment.
The table being defined as:
create table MyTable (
a int not null primary key,
b int,
c int,
description varchar (100)
)
The function MyTable_History will return the data from the table as existed at the @Date moment.
create function MyTable_History (@Date datetime)
returns @r table (a int primary key, b int, c int, Description varchar (100))
as
begin
insert into @r (a, b, c, Description)--make a copy of the table.
select a, b, c, Description
from MyTable
declare @inserted xml, @deleted xml
--We will apply all the changes made to the table in the reversed order
declare c cursor local for
select OldValues, NewValues
from DMLLogs
where DateCreated >= @Date
order by DMLLogID desc
open c
fetch next from c into @Deleted, @Inserted
while @@fetch_status = 0 begin
if @Inserted is not null and @Deleted is not null --update operation
update @r set
b = T.c.value ('@b', 'int'),
c = T.c.value ('@c', 'int'),
Description = T.c.value ('@Description', 'varchar (100)')
from @r R inner join @Deleted.nodes ('deleted') T(C)
on R.a = T.c.value ('@a', 'int')
if @Deletedis null --insert operation; must be deleted
delete from @r
from @r R inner join @Inserted.nodes ('inserted') T(C)
on R.a = T.c.value ('@a', 'int')
if @Inserted is null --delete operation; must be inserted back into the table
insert into @r (a, b, c, Description)
select T.c.value ('@a', 'int'),
T.c.value ('@b', 'int'),
T.c.value ('@c', 'int'),
T.c.value ('@Description', 'Varchar (100)')
from @Deleted.nodes ('deleted') T(C)
fetch next from c into @Deleted, @Inserted
end
close c
deallocate c
return
end
go
The shortcoming is that this function does not handle correctly cases where the primary key (column a) was changed as a result of a update operation.
Also it will be very slow on large tables. For those it will be more efficiently to directly update the table, as the following procedure does:
create procedure Recover_MyTable
@Date datetime
as
begin tran
declare @inserted xml, @deleted xml
declare c cursor local for
select OldValues, NewValues
from DMLLogs
where DateCreated >= @Date
order by DMLLogID desc
open c
fetch next from c into @Deleted, @Inserted
while @@fetch_status = 0 begin
if @Inserted is not null and @Deleted is not null --update operation
update MyTable set
b = T.c.value ('@b', 'int'),
c = T.c.value ('@c', 'int'),
Description = T.c.value ('@Description', 'varchar (100)')
from MyTable R inner join @Deleted.nodes ('deleted') T(C)
on R.a = T.c.value ('@a', 'int')
if @Deletedis null --insert operation; must be deleted
delete from MyTable
from MyTable R inner join @Inserted.nodes ('inserted') T(C)
on R.a = T.c.value ('@a', 'int')
if @Inserted is null --delete operation; must be inserted back into the table
insert into MyTable (a, b, c, Description)
select T.c.value ('@a', 'int'),
T.c.value ('@b', 'int'),
T.c.value ('@c', 'int'),
T.c.value ('@Description', 'Varchar (100)')
from @Deleted.nodes ('deleted') T(C)
fetch next from c into @Deleted, @Inserted
end
close c
deallocate c
commit tran
go
You can test the procedure using the following code:
begin tran
go
--load some data into the table
insert into MyTable (a, b, c, description)
select 1, 2, 3, 'first inserted'
union all
select 2, 3, 4, 'inserted'
waitfor delay '0:00:01' --wait a second because SQL has only 3 ms time resolution and we risk to have the same timestamp
declare @d datetime set @d = getdate() --store the time
waitfor delay '0:00:01'
-- change the data
insert into MyTable (a, b, c, description)
select 100, 2, 3, 'inserted'
union all
select 200, 2, 3, 'inserted'
update mytable set b = 5
delete from mytable where a = 2
select * from MyTable --watch the current data
exec Recover_MyTable @d --recover the old data
select * from MyTable --the changes are undone
go
rollback
That's it.
PS. Does anyone know how to insert tabs in the message ?