WorkaHolic

실행한 명령 되돌리기

2010. 4. 8. 22:35
반응형

--저자: Gleb Oufimtsev

 

/*************************************************************************/
/*         Library SQL-UNDO version 2.1                                  */
/*         Copyright (C) 2000 Oufimtsev Gleb                             */
/*                                                                       */
/*            gvu@newmail.ruhttp://www.gvu.newmail.ru                 */
/*            +7 (095) 178-40-92, Moscow, Russia                         */
/*************************************************************************/

/********************** Installation script ******************************/

CREATE TABLE HistoryLog (
 Id int NOT NULL PRIMARY KEY CLUSTERED ,
 IdOperation int NOT NULL ,
 ValueId int NOT NULL ,
 HLDate datetime NOT NULL ,
 Login sysname NOT NULL ,
 IdTableName int NOT NULL ,
 BatchMarker varchar (60) NULL
)
GO

CREATE TABLE HistoryLogColumns (
 Id int NOT NULL PRIMARY KEY CLUSTERED ,
 ColumnName sysname NOT NULL UNIQUE
)
GO

CREATE TABLE HistoryLogExcludedTables (
 Id int IDENTITY (1, 1) NOT NULL PRIMARY KEY CLUSTERED ,
 TableName sysname NOT NULL UNIQUE
)
GO

CREATE TABLE HistoryLogTables (
 Id int NOT NULL PRIMARY KEY CLUSTERED ,
 TableName sysname NOT NULL UNIQUE
)
GO

CREATE TABLE HistoryLogValue (
 Id int NOT NULL PRIMARY KEY CLUSTERED ,
 IdHistoryLog int NOT NULL ,
 OldValue varchar (4000) NULL ,
 NewValue varchar (4000) NULL ,
 IdColumn int NOT NULL
)
GO

 CREATE  INDEX idx_HistoryLog_1 ON HistoryLog(HLDate, Login, IdTableName)
 CREATE  INDEX idx_HistoryLog_2 ON HistoryLog(Login, IdTableName)
 CREATE  INDEX idx_HistoryLog_3 ON HistoryLog(ValueId, IdTableName)
 CREATE  INDEX idx_HistoryLog_4 ON HistoryLog(IdTableName, Login)
 CREATE  INDEX idx_HistoryLog_5 ON HistoryLog(BatchMarker)
GO

 CREATE  INDEX idx_HistoryLogValue_1 ON HistoryLogValue(IdHistoryLog, IdColumn)
 CREATE  INDEX idx_HistoryLogValue_2 ON HistoryLogValue(IdColumn)
GO

ALTER TABLE HistoryLog ADD
 CONSTRAINT FK_HistoryLog_HistoryLogTables
            FOREIGN KEY (IdTableName) REFERENCES HistoryLogTables (Id)
GO

ALTER TABLE HistoryLogValue ADD
 CONSTRAINT FK_HistoryLogValue_HistoryLog
            FOREIGN KEY (IdHistoryLog) REFERENCES HistoryLog (Id),
 CONSTRAINT FK_HistoryLogValue_HistoryLogColumns
            FOREIGN KEY (IdColumn) REFERENCES HistoryLogColumns (Id)
GO

GRANT SELECT, INSERT ON HistoryLogValue TO public
GO


/*************************************************************************/
/*         Library SQL-UNDO version 2.1                                  */
/*         Copyright (C) 2000 Oufimtsev Gleb                             */
/*                                                                       */
/*            gvu@newmail.ruhttp://www.gvu.newmail.ru                 */
/*            +7 (095) 178-40-92, Moscow, Russia                         */
/*************************************************************************/
CREATE PROCEDURE spHLCreateHistoryTrigger @TableName sysname AS

-- Do not use this procedure directly.
-- This is internal service for use by other procedures

set nocount on

declare @str nvarchar(4000)
declare @trname sysname
declare @CR nchar(1)
declare @cnt int

select @CR=NCHAR(10), @cnt=0

select @trname='tr_'+@TableName+'_HistoryLog_'+convert(varchar(8),GETDATE(),112)

select @str='/*************************************************************************/'+@CR
           +'/*         Library SQL-UNDO version 2.1                                  */'+@CR
           +'/*         Copyright (C) 2000 Oufimtsev Gleb                             */'+@CR
           +'/*                                                                       */'+@CR
           +'/*            gvu@newmail.ruhttp://www.gvu.newmail.ru                 */'+@CR
           +'/*            +7 (095) 178-40-92, Moscow, Russia                         */'+@CR
           +'/*************************************************************************/'+@CR
           +'CREATE TRIGGER '+@trname+' ON '+@TableName+' '+@CR
           +'FOR INSERT, UPDATE, DELETE '+@CR
           +'AS '+@CR
           +'/*                                                         */'+@CR
           +'/*     Trigger of registration of changes                  */'+@CR
           +'/*     Generated by procedure spHLRecreateTriggers         */'+@CR
           +'/*                                                         */'+@CR+@CR
           +'set nocount on '+@CR+@CR
           +'select * into #HL02dt from deleted '+@CR
           +'select * into #HL02it from inserted '+@CR
           +'exec spHLSaveOperation @@PROCID '+@CR

exec(@str)

select @cnt=@cnt+1, @str='Trigger '+@trname+' on table '+@TableName+' was added'

print @str

return @cnt

GO

/*************************************************************************/
/*         Library SQL-UNDO version 2.1                                  */
/*         Copyright (C) 2000 Oufimtsev Gleb                             */
/*                                                                       */
/*            gvu@newmail.ruhttp://www.gvu.newmail.ru                 */
/*            +7 (095) 178-40-92, Moscow, Russia                         */
/*************************************************************************/
CREATE PROCEDURE spHLDropHistoryTrigger @TableName sysname AS

-- Do not use this procedure directly.
-- This is internal service for use by other procedures

set nocount on

declare @str nvarchar(4000)
declare @trname sysname
declare @cnt int

select @cnt=0

while exists (select * from sysobjects where type='TR' and name like 'tr_'+@TableName+'_HistoryLog_%')
begin
  select @trname=name from sysobjects where type='TR' and name like 'tr_'+@TableName+'_HistoryLog_%'
  select @str='drop trigger '+@trname
  exec(@str)
  select @cnt=@cnt+1
  select @str='Trigger '+@trname+' on table '+@TableName+' was dropped'
  print @str
end

return @cnt

GO


/*************************************************************************/
/*         Library SQL-UNDO version 2.1                                  */
/*         Copyright (C) 2000 Oufimtsev Gleb                             */
/*                                                                       */
/*            gvu@newmail.ruhttp://www.gvu.newmail.ru                 */
/*            +7 (095) 178-40-92, Moscow, Russia                         */
/*************************************************************************/
CREATE PROCEDURE spHLRecreateTriggers @TableName sysname=NULL AS

-- Creation and recreation of triggers of record of log

-- If @TableName<>NULL, the trigger will be recreated only
--      in the table @TableName.

-- If @TableName=NULL, the trigger will be recreated on all
--      tables of a database, except for listed in HistoryLogExcludedTables.

set nocount on
set transaction isolation level SERIALIZABLE
set xact_abort on

begin tran

declare @msg varchar(255), @cnt1 int, @cnt2 int

print '         Library SQL-UNDO version 2.0'
print '         Copyright (C) 2000 Oufimtsev Gleb, Moscow, Russia'
print ''
print '             gvu@newmail.ruhttp://www.gvu.newmail.ru,  +7 (095) 178-40-92'
print ''

if @TableName is NOT NULL
  begin
    exec spHLDropHistoryTrigger @TableName
    exec spHLCreateHistoryTrigger @TableName
  end
else
  begin

    select @cnt1=0

    DECLARE TBL1 CURSOR LOCAL FAST_FORWARD FOR
      select name from sysobjects where type='U'
    OPEN TBL1
    FETCH TBL1 INTO @TableName
    WHILE @@FETCH_STATUS=0
    BEGIN
      exec @cnt2=spHLDropHistoryTrigger @TableName
      select @cnt1=@cnt1+@cnt2
      FETCH TBL1 INTO @TableName
    END
    CLOSE TBL1
    DEALLOCATE TBL1

    select @msg='Dropped: '+rtrim(convert(varchar(10),@cnt1))+' triggers'
    print '______________________'
    print @msg
    print ''

    select @cnt1=0

    DECLARE TBL2 CURSOR LOCAL FAST_FORWARD FOR
      select name from sysobjects where type='U'
         and name not in (select TableName from HistoryLogExcludedTables)
         and name not like 'HistoryLog%'
         and name<>'dtproperties'
    OPEN TBL2
    FETCH TBL2 INTO @TableName
    WHILE @@FETCH_STATUS=0
    BEGIN
      exec @cnt2=spHLCreateHistoryTrigger @TableName
      select @cnt1=@cnt1+@cnt2
      FETCH TBL2 INTO @TableName
    END
    CLOSE TBL2
    DEALLOCATE TBL2

    select @msg='Created: '+rtrim(convert(varchar(10),@cnt1))+' triggers'
    print '______________________'
    print @msg
    print ''

  end -- else

commit

GO


/*************************************************************************/
/*         Library SQL-UNDO version 2.1                                  */
/*         Copyright (C) 2000 Oufimtsev Gleb                             */
/*                                                                       */
/*            gvu@newmail.ruhttp://www.gvu.newmail.ru                 */
/*            +7 (095) 178-40-92, Moscow, Russia                         */
/*************************************************************************/
CREATE PROCEDURE spHLSeparateUndo @IdHistory int AS

-- Do not use this procedure directly.
-- This is internal service for use by other procedures

declare @TypeOperation int
declare @TableName sysname
declare @ValueId int
declare @IdTable int
declare @strValueId varchar(10)
declare @sql_header varchar(8000)
declare @sql_footer varchar(8000)
declare @Date datetime
declare @ColName sysname
declare @OldValue varchar(8000)
declare @ColType sysname
declare @ColNames varchar(8000)
declare @ColValues varchar(8000)
declare @CR char(1)

declare @errmsg varchar(255)

set nocount on

select @CR=CHAR(10)

select @TableName=t.TableName,
       @TypeOperation=h.IdOperation,
       @ValueId=h.ValueId,
       @strValueId=Rtrim(convert(varchar(10),h.ValueId)),
       @Date=h.HLDate,
       @IdTable=h.IdTableName
from HistoryLogTables t (nolock), HistoryLog h (nolock)
where h.IdTableName=t.Id
and h.Id=@IdHistory

if @TypeOperation=1 -- 岫?insert, 午成嶢, 哀飮孼 岫潗 delete
begin
  exec('delete from '+@TableName+' with (holdlock) where Id='+@strValueId
  if @@ERROR<>0 or @@ROWCOUNT<>1
  begin
    select @errmsg='Undo-runtime error'+@CR+'<<delete from '+@TableName+' with (holdlock) where Id='+@strValueId+'>>'
    raiserror(@errmsg,16,-1)
    return -100
  end 
end
if @TypeOperation=3 -- 岫?delete, 午成嶢, 哀飮孼 岫潗 insert
begin
 
  select @sql_footer='insert into '+@TableName+' with (holdlock) (Id' 

  if (select 0x80&col.status from syscolumns col (nolock)
        where name='Id' and id=OBJECT_ID(@TableName))=0x80
     select @sql_header='set IDENTITY_INSERT '+@TableName+' ON '+@CR
   else
     select @sql_header='' 

  declare COLSVALS cursor local fast_forward for
    select c.ColumnName, v.OldValue, t.name
    from HistoryLogColumns c (nolock), HistoryLogValue v (nolock),
         syscolumns col (nolock), systypes t (nolock)
    where v.IdColumn=c.Id
    and c.ColumnName=col.name
    and col.xtype=t.xusertype
    and col.id=OBJECT_ID(@TableName)
    and v.IdHistoryLog=@IdHistory
    and v.OldValue is not NULL
  open COLSVALS
  fetch COLSVALS into @ColName, @OldValue, @ColType
  while @@FETCH_STATUS=0
  begin

    select @ColNames=IsNull(@ColNames,'')+','+@ColName
    if @ColType in ('datetime','smalldatetime','char','varchar','nchar','nvarchar')
        select @OldValue=CHAR(39)+@OldValue+CHAR(39)
    select @ColValues=IsNull(@ColValues,'')+','+@OldValue
    fetch COLSVALS into @ColName, @OldValue, @ColType
  end
  close COLSVALS
  deallocate COLSVALS

  exec(@sql_header+@sql_footer+@ColNames+') values ('+@strValueId+@ColValues+')')
  if @@ERROR<>0 or @@ROWCOUNT<>1
  begin
    select @errmsg='Undo-runtime error'+@CR+'<<'+@sql_footer+@ColNames+') values ('+@strValueId+@ColValues+')'+'>>'
    raiserror(@errmsg,16,-1)
    return -100
  end 
end

if @TypeOperation=2 -- 岫?update, 午成嶢, 哀飮孼 岫潗 update
begin
  select @sql_header='update '+@TableName+' with (holdlock) set ' 
  select @sql_footer=' where Id='+@strValueId

  declare COLSVALS2 cursor local fast_forward for
    select c.ColumnName, v.OldValue, t.name
    from HistoryLogColumns c (nolock), HistoryLogValue v (nolock),
         syscolumns col (nolock), systypes t (nolock)
    where v.IdColumn=c.Id
    and c.ColumnName=col.name
    and col.xtype=t.xusertype
    and col.id=OBJECT_ID(@TableName)
    and v.IdHistoryLog=@IdHistory
  open COLSVALS2
  fetch COLSVALS2 into @ColName, @OldValue, @ColType
  while @@FETCH_STATUS=0
  begin
    if @OldValue is NULL select @OldValue='NULL'
    else if @ColType in ('datetime','smalldatetime','char','varchar','nchar','nvarchar')
        select @OldValue=CHAR(39)+@OldValue+CHAR(39)
    exec(@sql_header+@ColName+'='+@OldValue+@sql_footer)
    if @@ERROR<>0 or @@ROWCOUNT<>1
    begin
      select @errmsg='Undo-runtime error'+@CR+'<<'+@sql_header+@ColName+'='+@OldValue+@sql_footer+'>>'
      raiserror(@errmsg,16,-1)
      close COLSVALS2
      deallocate COLSVALS2
      return -100
    end  
    fetch COLSVALS2 into @ColName, @OldValue, @ColType
  end
  close COLSVALS2
  deallocate COLSVALS2
end

delete from v from HistoryLog h, HistoryLogValue v
where v.IdHistoryLog=h.Id
and h.ValueId=@ValueId
and (h.Login=SUSER_SNAME() or IS_MEMBER('db_owner')=1)
and h.HLDate>=@Date
and h.IdTableName=@IdTable

delete from HistoryLog
where ValueId=@ValueId
and (Login=SUSER_SNAME() or IS_MEMBER('db_owner')=1)
and HLDate>=@Date
and IdTableName=@IdTable

GO


/*************************************************************************/
/*         Library SQL-UNDO version 2.1                                  */
/*         Copyright (C) 2000 Oufimtsev Gleb                             */
/*                                                                       */
/*            gvu@newmail.ruhttp://www.gvu.newmail.ru                 */
/*            +7 (095) 178-40-92, Moscow, Russia                         */
/*************************************************************************/
CREATE PROCEDURE spHLSetColumnInfo

-- Do not use this procedure directly.
-- This is internal service for use by other procedures

   @TypeOperation int,
   @ColName sysname,
   @ValueId int,
   @IdHistory int

 AS

declare @CR char(1)
declare @IdColumn int

declare @sql_part1 varchar(8000)
declare @sql_part2 varchar(8000)
declare @sql_part3 varchar(8000)

set nocount on

select @CR=CHAR(10)

select @IdColumn=Id from HistoryLogColumns where ColumnName=@ColName
if @IdColumn is NULL
begin
  begin tran
    select @IdColumn=IsNull(max(Id),0)+1 from HistoryLogColumns (updlock, holdlock)
    insert into HistoryLogColumns (Id, ColumnName) values (@IdColumn, @ColName)
  commit
end
select @sql_part1='declare @o varchar(8000), @n varchar(8000), @IdHistoryValue int '+@CR

if @TypeOperation=1
begin
  select @sql_part2=           'select @n=Rtrim(convert(varchar(8000),'+@ColName+')) '+@CR
  select @sql_part2=@sql_part2+'from #HL02it where Id='+rtrim(convert(varchar(10),@ValueId))+' '+@CR
  select @sql_part2=@sql_part2+'if @n is not NULL '+@CR  
end

if @TypeOperation=3
begin
  select @sql_part2=           'select @o=Rtrim(convert(varchar(8000),'+@ColName+')) '+@CR
  select @sql_part2=@sql_part2+'from #HL02dt where Id='+rtrim(convert(varchar(10),@ValueId))+' '+@CR
  select @sql_part2=@sql_part2+'if @o is not NULL '+@CR  
end

if @TypeOperation=2
begin
  select @sql_part2=           'select @n=Rtrim(convert(varchar(8000),'+@ColName+')) '+@CR
  select @sql_part2=@sql_part2+'from #HL02it where Id='+rtrim(convert(varchar(10),@ValueId))+' '+@CR
  select @sql_part2=@sql_part2+'select @o=Rtrim(convert(varchar(8000),'+@ColName+')) '+@CR
  select @sql_part2=@sql_part2+'from #HL02dt where Id='+rtrim(convert(varchar(10),@ValueId))+' '+@CR
  select @sql_part2=@sql_part2+'if (@n<>@o) or (@n is NULL and @o is not NULL) or (@n is not NULL and @o is NULL) '+@CR
end

select @sql_part3=           'begin '+@CR
select @sql_part3=@sql_part3+'  begin tran '+@CR
select @sql_part3=@sql_part3+'    select @IdHistoryValue=IsNull(max(Id),0)+1 from HistoryLogValue (updlock, holdlock) '+@CR
select @sql_part3=@sql_part3+'    insert HistoryLogValue (Id,IdHistoryLog,NewValue,OldValue,IdColumn) '+@CR
select @sql_part3=@sql_part3+'         values (@IdHistoryValue,'+rtrim(convert(varchar(10),@IdHistory))
select @sql_part3=@sql_part3+'               ,@n,@o,'+rtrim(convert(varchar(10),@IdColumn))+') '+@CR
select @sql_part3=@sql_part3+'  commit '+@CR
select @sql_part3=@sql_part3+'end '

exec(@sql_part1+@sql_part2+@sql_part3)

GO


/*************************************************************************/
/*         Library SQL-UNDO version 2.1                                  */
/*         Copyright (C) 2000 Oufimtsev Gleb                             */
/*                                                                       */
/*            gvu@newmail.ruhttp://www.gvu.newmail.ru                 */
/*            +7 (095) 178-40-92, Moscow, Russia                         */
/*************************************************************************/
CREATE PROCEDURE spHLSetHistory

-- Do not use this procedure directly.
-- This is internal service for use by other procedures
 
    @TableName sysname,
    @TypeOperation int,
    @ValueId int,
    @BatchMarker varchar(60)=NULL

 AS

declare @IdTable int
declare @IdHistory int

set nocount on

select @IdTable=Id from HistoryLogTables where TableName=@TableName
if @IdTable is NULL
begin
  begin tran
    select @IdTable=IsNull(max(Id),0)+1 from HistoryLogTables (updlock, holdlock)
    insert into HistoryLogTables (Id, TableName) values (@IdTable, @TableName)
  commit
end

begin tran
  select @IdHistory=IsNull(max(Id),0)+1 from HistoryLog (updlock, holdlock)
  insert into HistoryLog(Id, IdTableName, IdOperation, ValueId, HLDate, Login, BatchMarker)
       values (@IdHistory, @IdTable, @TypeOperation, @ValueId, GETDATE(), SUSER_SNAME(), @BatchMarker)
commit

return @IdHistory

GO


/*************************************************************************/
/*         Library SQL-UNDO version 2.1                                  */
/*         Copyright (C) 2000 Oufimtsev Gleb                             */
/*                                                                       */
/*            gvu@newmail.ruhttp://www.gvu.newmail.ru                 */
/*            +7 (095) 178-40-92, Moscow, Russia                         */
/*************************************************************************/
CREATE PROCEDURE spHLSaveOperation @IdTrigger int AS

-- Do not use this procedure directly.
-- This is internal service for use by triggers

declare @IdHistory int 

declare @ValueId int

declare @InsCount int
declare @DelCount int

declare @TypeOperation int  -- 1-insert, 2-update, 3-delete
declare @TableName sysname
declare @IdTable int
declare @ColName sysname

declare @BatchMarker varchar(60)

set nocount on

select @InsCount=0, @DelCount=0

select @DelCount=count(*) from #HL02dt
select @InsCount=count(*) from #HL02it

if @InsCount=0 and @DelCount=0  RETURN 
   --It is possible, that inserted and deleted have 0 records simultaneously.

if @InsCount>10 or @DelCount>10  RETURN 
   --Mass operation - administrative operation. To not serve..

if @InsCount>0 and @DelCount=0 select @TypeOperation=1
  else if @InsCount>0 and @DelCount>0 select @TypeOperation=2
    else if @InsCount=0 and @DelCount>0 select @TypeOperation=3

select @TableName=t.name, @IdTable=t.id
from sysobjects t, sysobjects tr
where tr.parent_obj=t.id
and t.type='U'
and tr.type='TR'
and tr.id=@IdTrigger

select @BatchMarker=convert(varchar(3),spid)+'|'+convert(varchar(3),suid)+'|'
                   +convert(varchar(23),login_time,121)+'|'+convert(varchar(23),last_batch,121)
from master..sysprocesses where spid=@@SPID

if @DelCount=1 or @InsCount=1    -- Was change only 1 row
                                 -- Facilitated fast algorithm
begin

  select @ValueId=Id from #HL02it
  if @ValueId is NULL select @ValueId=Id from #HL02dt

    exec @IdHistory=spHLSetHistory @TableName, @TypeOperation, @ValueId, @BatchMarker

    declare COL cursor local fast_forward for
       select c.name from syscolumns c, systypes t
       where c.xusertype=t.xusertype
       and c.id=@IdTable
       and c.name<>'Id'
       and t.name<>'timestamp'
    open COL
    fetch COL into @ColName
    while @@FETCH_STATUS=0
    begin
      exec spHLSetColumnInfo @TypeOperation, @ColName, @ValueId, @IdHistory
      fetch COL into @ColName
    end
    close COL
    deallocate COL
 
end

if @DelCount>1 or @InsCount>1    -- More than one row is changed
begin
    declare RWS cursor local fast_forward for
       select distinct Id from #HL02it
       union
       select distinct Id from #HL02dt
    open RWS
    fetch RWS into @ValueId
    while @@FETCH_STATUS=0
    begin

      exec @IdHistory=spHLSetHistory @TableName, @TypeOperation, @ValueId, @BatchMarker

      declare MCOL cursor local fast_forward for
         select c.name from syscolumns c, systypes t
         where c.xusertype=t.xusertype
         and c.id=@IdTable
         and c.name<>'Id'
         and t.name<>'timestamp'
      open MCOL
      fetch MCOL into @ColName
      while @@FETCH_STATUS=0
      begin

        exec spHLSetColumnInfo @TypeOperation, @ColName, @ValueId, @IdHistory

        fetch MCOL into @ColName
      end
      close MCOL
      deallocate MCOL

      fetch RWS into @ValueId
    end
    close RWS
    deallocate RWS

end

GO

GRANT  EXECUTE  ON spHLSaveOperation TO public
GO


/*************************************************************************/
/*         Library SQL-UNDO version 2.1                                  */
/*         Copyright (C) 2000 Oufimtsev Gleb                             */
/*                                                                       */
/*            gvu@newmail.ruhttp://www.gvu.newmail.ru                 */
/*            +7 (095) 178-40-92, Moscow, Russia                         */
/*************************************************************************/
CREATE PROCEDURE spHLUndo @IdHistory int AS

-- This is main working procedure for use by users

-- Undoes action of the user, if not a lot of time has passed

-- @IdHistory - specifies concrete operation in HistoryLog

declare @BatchMarker varchar(60) 
declare @retval int

set nocount on
set xact_abort on

if exists (select * from HistoryLog (nolock)
              where Id=@IdHistory and IS_MEMBER('db_owner')=0)
begin
  raiserror('You have no the right to undo operation of other user',16,-1)
  return -100
end

select @BatchMarker=BatchMarker from HistoryLog (nolock) where Id=@IdHistory

create table #histkeys (Id int not NULL Primary Key CLUSTERED,
                        IdTable int not NULL,
                        ValueId int not NULL)

if @BatchMarker is NULL
     insert into #histkeys (Id,IdTable,ValueId)
     select Id, IdTableName, ValueId from HistoryLog (nolock)
          where Id=@IdHistory
  else
     insert into #histkeys (Id,IdTable,ValueId)
     select Id, IdTableName, ValueId from HistoryLog (nolock)
          where BatchMarker=@BatchMarker

if exists (select * from #histkeys (nolock)
           where not exists (select * from HistoryLogValue (nolock)
                              where IdHistoryLog=#histkeys.Id))
begin
  raiserror('There is no kept information on importance of fields',16,-1)
  return -100
end

if exists (select * from #histkeys h (nolock)
           where exists (select * from HistoryLog sh (NOLOCK)
                          where sh.ValueId=h.ValueId
                          and sh.IdTableName=h.IdTable
                          and sh.Id>h.Id
                          and sh.Id not in (select h1.Id from #histkeys h1 (nolock))))
begin
  raiserror('Attempt to undo operations later already changed',16,-1)
  return -100
end

declare RECS cursor local fast_forward for
    select Id from #histkeys (nolock) order by 1 DESC
open RECS
fetch RECS into @IdHistory

begin tran

  while @@FETCH_STATUS=0
  begin
    exec @retval=spHLSeparateUndo @IdHistory
    if @@ERROR<>0 or @retval<0
    begin
      raiserror('Error of a undo of batch',16,-1)
      close RECS
      deallocate RECS
      if @@TRANCOUNT>0 rollback 
      return @retval
    end
    fetch RECS into @IdHistory
  end

commit

close RECS
deallocate RECS

drop table #histkeys

GO

/*************************************************************************/
/*         Library SQL-UNDO version 2.1                                  */
/*         Copyright (C) 2000 Oufimtsev Gleb                             */
/*                                                                       */
/*            gvu@newmail.ruhttp://www.gvu.newmail.ru                 */
/*            +7 (095) 178-40-92, Moscow, Russia                         */
/*************************************************************************/
CREATE PROCEDURE spHLUndoLastOperation AS

-- This is the second basic procedure for use by users
-- Undoes last action of the current user, if not a lot of time has passed

declare @IdHistory int
declare @mdate datetime

set nocount on

select @IdHistory=max(Id) from HistoryLog where Login=SUSER_SNAME()
select @mdate=HLDate from HistoryLog where Id=@IdHistory

if DATEDIFF(mi,@mdate,GETDATE())>20
begin
  raiserror('You made changes for a long time',16,-1)
  return -100
end

if @IdHistory is not NULL exec spHLUndo @IdHistory

GO

반응형