下面直接上代码(copy到你的数据库里面直接就可以运行):

  

  1. CREATE PROCEDURE [dbo].[SP_UPDATE_LOG]
  2. @TABLENAME VARCHAR(50)
  3. AS
  4. BEGIN
  5. SET NOCOUNT ON;
  6. IF NOT EXISTS(SELECT * FROM sys.tables WHERE NAME = @TABLENAME AND TYPE = 'U' )
  7. BEGIN
  8. PRINT'ERROR:not exist table '+@TABLENAME
  9. RETURN
  10. END
  11. IF (@TABLENAME LIKE'BACKUP_%' OR @TABLENAME='UPDATE_LOG' )
  12. BEGIN
  13. --PRINT'ERROR:not exist table '+@TABLENAME
  14. RETURN
  15. END
  16. --================================判断是否存在 UPDATE_LOG 表============================
  17. IF NOT EXISTS(SELECT * FROM sys.tables WHERE NAME = 'UPDATE_LOG' AND TYPE = 'U')
  18. CREATE TABLE UPDATE_LOG
  19. (
  20. UpdateGUID VARCHAR(36),
  21. UpdateTime DATETIME,
  22. TableName varchar(20),
  23. UpdateType varchar(6),
  24. RollBackSQL varchar(MAX),
  25. ExecSQL VARCHAR(500)
  26. )
  27. --=================================判断是否存在 BACKUP_ 表================================
  28. IF NOT EXISTS(SELECT * FROM sys.tables WHERE NAME = 'BACKUP_'+@TABLENAME AND TYPE = 'U')
  29. BEGIN
  30. DECLARE test_Cursor CURSOR FOR
  31. SELECT COLUMN_NAME,DATA_TYPE,CHARACTER_MAXIMUM_LENGTH FROM INFORMATION_SCHEMA.columns
  32. WHERE TABLE_NAME=@TABLENAME
  33. OPEN test_Cursor
  34. DECLARE @SQLTB NVARCHAR(MAX)=''
  35. DECLARE @COLUMN_NAME NVARCHAR(50),@DATA_TYPE VARCHAR(20),@CHARACTER_MAXIMUM_LENGTH INT
  36. FETCH NEXT FROM test_Cursor INTO @COLUMN_NAME,@DATA_TYPE,@CHARACTER_MAXIMUM_LENGTH
  37. WHILE @@FETCH_STATUS=0
  38. BEGIN
  39. SET @SQLTB=@SQLTB+'['+@COLUMN_NAME+'] '+@DATA_TYPE+CASE ISNULL(@CHARACTER_MAXIMUM_LENGTH,0) WHEN 0 THEN '' WHEN -1 THEN '(MAX)' ELSE'('+CAST(@CHARACTER_MAXIMUM_LENGTH AS VARCHAR(10))+')' END+','
  40. FETCH NEXT FROM test_Cursor INTO @COLUMN_NAME,@DATA_TYPE,@CHARACTER_MAXIMUM_LENGTH
  41. END
  42. SET @SQLTB='CREATE TABLE BACKUP_'+@TABLENAME+' (UpdateGUID varchar(36),UpdateType Varchar(10),'+SUBSTRING(@SQLTB,1,LEN(@SQLTB)-1)+')'
  43. EXEC (@SQLTB)
  44. CLOSE test_Cursor
  45. DEALLOCATE test_Cursor
  46. END
  47. --======================================判断是否存在 UPDATE 触发器=========================
  48. IF NOT EXISTS(SELECT * FROM sys.objects WHERE NAME = 'tg_'+@TABLENAME+'_Update' AND TYPE = 'TR')
  49. BEGIN
  50. DECLARE @SQLTR NVARCHAR(MAX)
  51. SET @SQLTR='
  52. CREATE TRIGGER tg_'+@TABLENAME+'_Update
  53. ON '+@TABLENAME+'
  54. AFTER Update,Delete,Insert
  55. AS
  56. BEGIN
  57. SET NOCOUNT ON;
  58. --==============================获取GUID==========================================
  59. DECLARE @NEWID VARCHAR(36)=NEWID()
  60.  
  61. --===========================将删掉或新增的数据插入备份表=========================
  62. DECLARE @ROWCOUNT INT
  63. INSERT INTO [dbo].[BACKUP_'+@TABLENAME+']
  64. SELECT @NEWID,''DELETE'',* FROM deleted
  65. SET @ROWCOUNT=@@ROWCOUNT
  66. IF @ROWCOUNT>0
  67. BEGIN
  68. INSERT INTO [dbo].[BACKUP_'+@TABLENAME+']
  69. SELECT @NEWID,''INSERT'',* FROM inserted
  70. END
  71. ELSE
  72. BEGIN
  73. INSERT INTO [dbo].[BACKUP_'+@TABLENAME+']
  74. SELECT @NEWID,''INSERT'',* FROM inserted
  75. SET @ROWCOUNT=@@ROWCOUNT
  76. END
  77.  
  78. --==============================记录日志和回滚操作的SQL===========================
  79.  
  80. --******************生成插入语句用到的列名(需避开自增字段)********************
  81. DECLARE @COLUMN1 NVARCHAR(MAX)=''''
  82. SELECT @COLUMN1+='',[''+COLUMN_NAME+'']'' FROM INFORMATION_SCHEMA.columns
  83. WHERE TABLE_NAME='''+@TABLENAME+'''
  84. AND COLUMNPROPERTY(OBJECT_ID('''+@TABLENAME+'''),COLUMN_NAME,''IsIdentity'')<>1 --非自增字段
  85. SET @COLUMN1=SUBSTRING(@COLUMN1,2,LEN(@COLUMN1))
  86.  
  87. --*******************动态定义变量、删除条件匹配的列********************
  88. DECLARE @DECLARE VARCHAR(MAX)='''',@INTODECLARE VARCHAR(MAX)='''',@WHERE VARCHAR(MAX)='''',@COLUMN2 VARCHAR(MAX)=''''
  89. SELECT @DECLARE+=''@''+COLUMN_NAME+'' ''+DATA_TYPE+CASE ISNULL(CAST(CHARACTER_OCTET_LENGTH AS VARCHAR(10)),'''') WHEN '''' THEN '','' WHEN ''-1'' THEN ''(MAX),'' ELSE ''(''+CAST(CHARACTER_OCTET_LENGTH AS VARCHAR(10))+''),'' END,
  90. @INTODECLARE+=''@''+COLUMN_NAME+'','',
  91. @COLUMN2+=''[''+COLUMN_NAME+''],'' ,
  92. @WHERE += ''ISNULL(''+ COLUMN_NAME+'','''''''')=ISNULL(@''+COLUMN_NAME+'','''''''') AND ''
  93. FROM INFORMATION_SCHEMA.columns
  94. WHERE TABLE_NAME='''+@TABLENAME+'''
  95. SET @DECLARE=LEFT(@DECLARE,LEN(@DECLARE)-1)
  96. SET @INTODECLARE=LEFT(@INTODECLARE,LEN(@INTODECLARE)-1)
  97. SET @COLUMN2=LEFT(@COLUMN2,LEN(@COLUMN2)-1)
  98. SET @WHERE= LEFT(@WHERE,LEN(@WHERE)-3)
  99.  
  100. --*******************判断是否还原当前表的最近一次操作*******************
  101. DECLARE @SQL_ISLAST VARCHAR(MAX)=''
  102. SET NOCOUNT ON
  103. DECLARE @maxdate datetime
  104. SELECT @maxdate=max(updatetime) FROM UPDATE_LOG WHERE TableName='''''+@TABLENAME+'''''
  105. IF NOT EXISTS(SELECT 1 FROM UPDATE_LOG WHERE UpdateTime=@maxdate AND UPDATEGUID=''''''+@NEWID+'''''')
  106. BEGIN
  107. DECLARE @MAXGUID VARCHAR(50)
  108. SELECT @MAXGUID=UPDATEGUID FROM UPDATE_LOG WHERE UpdateTime=@maxdate
  109. PRINT ''''此操作并非最近一次操作,请逐步还原,此表最近一次操作的GUID是:''''+@MAXGUID
  110. RETURN
  111. END
  112. ''
  113.  
  114. --********************还原insert和update操作用到的SQL*******************
  115.  
  116. DECLARE @SQL_DELETE VARCHAR(MAX)=''
  117. SET ROWCOUNT 1 --设定相同条件下只删除1行
  118. DECLARE Cursor_ CURSOR FOR
  119. SELECT ''+@COLUMN2+'' FROM BACKUP_'+@TABLENAME+' WHERE UPDATEGUID= ''''''+@NEWID+'''''' AND UpdateType=''''INSERT''''
  120. OPEN Cursor_
  121. DECLARE ''+@DECLARE+''
  122. FETCH NEXT FROM Cursor_ INTO ''+@INTODECLARE+''
  123. WHILE @@FETCH_STATUS=0
  124. BEGIN
  125. DELETE FROM '+@TABLENAME+' WHERE ''+@WHERE+''
  126. FETCH NEXT FROM Cursor_ INTO ''+@INTODECLARE+''
  127. END
  128. CLOSE Cursor_
  129. DEALLOCATE Cursor_
  130. SET ROWCOUNT 0
  131. ''
  132.  
  133. --*********************还原delete和update操作用到的SQL*******************
  134.  
  135. DECLARE @SQL_INSERT VARCHAR(MAX)=''
  136. INSERT INTO '+@TABLENAME+' SELECT ''+@COLUMN1+'' FROM BACKUP_'+@TABLENAME+' WHERE UPDATEGUID=''''''+@NEWID+'''''' AND UpdateType=''''DELETE''''
  137. ''
  138.  
  139. --*********************还原操作之后把备份表和log表的记录删掉*************
  140.  
  141. DECLARE @SQL_DELGUID VARCHAR(MAX)=''
  142. DELETE FROM BACKUP_'+@TABLENAME+' WHERE UPDATEGUID IN(SELECT UPDATEGUID FROM UPDATE_LOG WHERE UpdateTime>=@maxdate AND TableName='''''+@TABLENAME+''''')
  143. DELETE FROM UPDATE_LOG WHERE UpdateTime>=@maxdate AND TableName='''''+@TABLENAME+'''''
  144. PRINT ''''回滚操作执行成功,共恢复 ''+CAST(@ROWCOUNT AS VARCHAR(10))+'' 条记录''''
  145. SET NOCOUNT OFF
  146. ''
  147.  
  148. --*********************执行还原操作的SQL**********************************
  149.  
  150. DECLARE @EXECSQL VARCHAR(500)=''
  151. DECLARE @SQL VARCHAR(MAX)
  152. SELECT @SQL=ROLLBACKSQL FROM UPDATE_LOG WHERE UPDATEGUID=''''''+@NEWID+''''''
  153. EXEC(@SQL)
  154. ''
  155.  
  156. --==============================判断执行的哪种操作方式=================================
  157.  
  158. DECLARE @DoType VARCHAR(MAX)=''UPDATE''
  159. IF NOT EXISTS(SELECT 1 FROM deleted)
  160. SET @DoType=''INSERT''
  161. IF NOT EXISTS(SELECT 1 FROM inserted)
  162. SET @DoType=''DELETE''
  163. IF NOT EXISTS(SELECT 1 FROM deleted) AND NOT EXISTS(SELECT 1 FROM inserted)
  164. RETURN
  165. IF @DoType=''UPDATE''
  166. BEGIN
  167. INSERT INTO [dbo].[UPDATE_LOG]
  168. SELECT @NEWID,GETDATE(),'''+@TABLENAME+''',''UPDATE'',@SQL_ISLAST+@SQL_DELETE+@SQL_INSERT+@SQL_DELGUID,@EXECSQL
  169. RETURN
  170. END
  171. IF @DoType=''DELETE''
  172. BEGIN
  173. INSERT INTO [dbo].[UPDATE_LOG]
  174. SELECT @NEWID,GETDATE(),'''+@TABLENAME+''',''DELETE'',@SQL_ISLAST+@SQL_INSERT+@SQL_DELGUID,@EXECSQL
  175. RETURN
  176. END
  177. IF @DoType=''INSERT''
  178. BEGIN
  179. INSERT INTO [dbo].[UPDATE_LOG]
  180. SELECT @NEWID,GETDATE(),'''+@TABLENAME+''',''INSERT'',@SQL_ISLAST+@SQL_DELETE+@SQL_DELGUID,@EXECSQL
  181. RETURN
  182. END
  183. END
  184. '
  185. EXEC (@SQLTR)
  186. END
  187. END

运行这段代码,你会创建一个存储过程,下面来建一个测试表简单测一下这个存储过程的功能吧:

  1. CREATE TABLE test(
  2. [id] [int] NULL,
  3. [name] [varchar](10) NULL
  4. )
  5.  
  6. INSERT INTO test
  7. SELECT 1,'a'
  8. UNION ALL
  9. SELECT 2,'b'
  10. UNION ALL
  11. SELECT 3,'c'
  12. UNION ALL
  13. SELECT 4,'d'
  14. UNION ALL
  15. SELECT 5,'a'
  16. UNION ALL
  17. SELECT 6,'b'
  18.  
  19. SELECT * FROM test

检查一下,表建好了:

接着执行存储过程给test表添加回滚日志:

  1. EXEC SP_UPDATE_LOG 'test' --给test表建立update回滚日志
  2. SELECT * FROM [dbo].[BACKUP_test] --test表数据备份
  3. SELECT * FROM [dbo].[UPDATE_LOG] --update操作记录

这时候你会发现生成了两张表:backup_test 和 update_log,包括test表下建立了触发器,backup_test是test表的备份表,由test表专用,update_log表是所有建立update回滚日志的表所公用的。这个表里面记录每张表操作的时间,做了何种操作,包括执行回滚的SQL:

下面测一下回滚的功能吧,我要把test表改得面目全非,然后再执行回滚:

update 操作被我一不小心执行了两次,变成了这副德行,下面我开始还原操作,先查询下update_log这张表:

产生了两条操作记录,copy ExecSQL里面的SQL语句执行,注意要先执行时间最近的操作记录,一步一步还原:

还原一步之后变成了这个样子,下面再还原一步:

到目前为止看下完全还原了吧~

好了,这个update的回滚日志也该介绍完了~终于打完了,手好累啊~~

PS:执行delete和insert操作也是一样的回滚步骤

原文:https://blog.csdn.net/Wikey_Zhang/article/details/73994020

[转]SQLServer添加UPDATE回滚日志(update/delete/insert)的更多相关文章

  1. MySQL中的重做日志(redo log),回滚日志(undo log),以及二进制日志(binlog)的简单总结

    MySQL中有六种日志文件,分别是:重做日志(redo log).回滚日志(undo log).二进制日志(binlog).错误日志(errorlog).慢查询日志(slow query log).一 ...

  2. python logging 实现的进程安全的文件回滚日志类

    python标准库中的logging模块在记录日志时经常会用到,但在实际使用发现它自带的用于本地日志回滚的类 logging.handlers.RotatingFileHandler 在多进程环境下会 ...

  3. mysql回滚日志

    一.回滚日志(undo log) 1.作用 保存了事务发生之前的数据的一个版本,可以用于回滚,同时可以提供多版本并发控制下的读(MVCC),也即非锁定读 2.内容 逻辑格式的日志,在执行undo的时候 ...

  4. Log4net入门(回滚日志文件篇)

    在上一篇Log4net(日志文件篇)中,我们使用"log4net.Appender.FileAppender"将日志信息输出到一个单一的文件中,随着应用程序的持续使用,该日志文件会 ...

  5. sqlserver事务与回滚

    如果要在Production执行数据改动必须小心,可以使用事务提前验证一下自己写的SQL是不是你期望的.尤其是Update的where 条件有问题的话,跟新的记录就会超出预期的范围.如下面的语句,一着 ...

  6. sprint test 添加事务回滚机制

    1.原因: 单元测试的时候频繁操作数据库需要修改很多数据,造成不必要的操作,添加事务之后就可以重复对一条数据进行操作,并且在返回结果后进行回滚. 2.解决: 原先继承的是  AbstractJUnit ...

  7. Quartz:Quartz添加事务回滚报错

    自动任务类: @PersistJobDataAfterExecution @DisallowConcurrentExecution public class ReCodeBack implements ...

  8. 两表关联更新,用于update 回滚

    create table test1 as select * from dba_objects; create table test2 as select * from dba_objects; cr ...

  9. SQLite中的WAL机制详细介绍-与回滚日志原理

    一.什么是WAL? WAL的全称是Write Ahead Logging,它是很多数据库中用于实现原子事务的一种机制,SQLite在3.7.0版本引入了该特性. 二.WAL如何工作? 在引入WAL机制 ...

随机推荐

  1. canvas 绘制图像

    结果: 代码: <!DOCTYPE html> <html> <head lang="en"> <meta charset="U ...

  2. Django 碎片集合

    命令行创建Django项目 熟记建立django命令:django-admin startproject xx   (start   project) 目录介绍 manage.py 文件是用来管理文件 ...

  3. MyBatis典型的错误org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)

    XXXmapper.java(接口) XXXmapper.xml(结果集映射) //此两个文件要在统一包下,且xml中的namespace是唯一的,为了区分须写成 该xml的全路径

  4. c printf打印格式

    关于小数点位数的举例:  <pre lang="c" escaped="true">#include <stdio.h> /* 当fah ...

  5. linux C使用strerror来追查错误信息

    最近工作中有个需求:程序将文件进行处理,然后将处理完毕的文件挪走.我用了rename函数来挪动文件,可是在docker化的环境中,文件却无法挪动.不知道什么原因.现在,对程序进行调整,如果rename ...

  6. linux之文件操作

    1. 文件操作思维导图 2. linux系统目录结构及简单说明 linux目录图: root --- 启动Linux时使用的一些核心文件.如操作系统内核.引导程序Grub等. home --- 存储普 ...

  7. Chrome 的书签太多如何分类整理比较好

    对于Chrome书签太多,如何进行分类整理,下面给出几种方法~ 工具/原料 电脑 Chrome浏览器 delicious.Diigo等 方法/步骤1 1.把书签中常用网站只保存LOGO,放在书签栏最前 ...

  8. idea操作

    archetypeCatalog  internal 1字体: 2编码 http://blog.csdn.net/frankcheng5143/article/details/50779149 3部署 ...

  9. JGroups 入门实践

    前言 JGroups是一个开源的纯java编写的可靠的群组通讯工具.其工作模式基于IP多播,但可以在可靠性和群组成员管理上进行扩展.其结构上设计灵活,提供了一种灵活兼容多种协议的协议栈. JGroup ...

  10. 数据流重定向和管道命令, grep, tr,sort, wc, cut,split,tee,sleep(shell 02)

    主要内容 1.标准输入输出和错误 2.管道命令和 grep, tr,sort, wc, cut,split,tee,sleep 标准输入输出和错误 标准输入(stdin) 是指令数据的输入,代码为0, ...