[转]SQLServer添加UPDATE回滚日志(update/delete/insert)
下面直接上代码(copy到你的数据库里面直接就可以运行):
- CREATE PROCEDURE [dbo].[SP_UPDATE_LOG]
- @TABLENAME VARCHAR(50)
- AS
- BEGIN
- SET NOCOUNT ON;
- IF NOT EXISTS(SELECT * FROM sys.tables WHERE NAME = @TABLENAME AND TYPE = 'U' )
- BEGIN
- PRINT'ERROR:not exist table '+@TABLENAME
- RETURN
- END
- IF (@TABLENAME LIKE'BACKUP_%' OR @TABLENAME='UPDATE_LOG' )
- BEGIN
- --PRINT'ERROR:not exist table '+@TABLENAME
- RETURN
- END
- --================================判断是否存在 UPDATE_LOG 表============================
- IF NOT EXISTS(SELECT * FROM sys.tables WHERE NAME = 'UPDATE_LOG' AND TYPE = 'U')
- CREATE TABLE UPDATE_LOG
- (
- UpdateGUID VARCHAR(36),
- UpdateTime DATETIME,
- TableName varchar(20),
- UpdateType varchar(6),
- RollBackSQL varchar(MAX),
- ExecSQL VARCHAR(500)
- )
- --=================================判断是否存在 BACKUP_ 表================================
- IF NOT EXISTS(SELECT * FROM sys.tables WHERE NAME = 'BACKUP_'+@TABLENAME AND TYPE = 'U')
- BEGIN
- DECLARE test_Cursor CURSOR FOR
- SELECT COLUMN_NAME,DATA_TYPE,CHARACTER_MAXIMUM_LENGTH FROM INFORMATION_SCHEMA.columns
- WHERE TABLE_NAME=@TABLENAME
- OPEN test_Cursor
- DECLARE @SQLTB NVARCHAR(MAX)=''
- DECLARE @COLUMN_NAME NVARCHAR(50),@DATA_TYPE VARCHAR(20),@CHARACTER_MAXIMUM_LENGTH INT
- FETCH NEXT FROM test_Cursor INTO @COLUMN_NAME,@DATA_TYPE,@CHARACTER_MAXIMUM_LENGTH
- WHILE @@FETCH_STATUS=0
- BEGIN
- 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+','
- FETCH NEXT FROM test_Cursor INTO @COLUMN_NAME,@DATA_TYPE,@CHARACTER_MAXIMUM_LENGTH
- END
- SET @SQLTB='CREATE TABLE BACKUP_'+@TABLENAME+' (UpdateGUID varchar(36),UpdateType Varchar(10),'+SUBSTRING(@SQLTB,1,LEN(@SQLTB)-1)+')'
- EXEC (@SQLTB)
- CLOSE test_Cursor
- DEALLOCATE test_Cursor
- END
- --======================================判断是否存在 UPDATE 触发器=========================
- IF NOT EXISTS(SELECT * FROM sys.objects WHERE NAME = 'tg_'+@TABLENAME+'_Update' AND TYPE = 'TR')
- BEGIN
- DECLARE @SQLTR NVARCHAR(MAX)
- SET @SQLTR='
- CREATE TRIGGER tg_'+@TABLENAME+'_Update
- ON '+@TABLENAME+'
- AFTER Update,Delete,Insert
- AS
- BEGIN
- SET NOCOUNT ON;
- --==============================获取GUID==========================================
- DECLARE @NEWID VARCHAR(36)=NEWID()
- --===========================将删掉或新增的数据插入备份表=========================
- DECLARE @ROWCOUNT INT
- INSERT INTO [dbo].[BACKUP_'+@TABLENAME+']
- SELECT @NEWID,''DELETE'',* FROM deleted
- SET @ROWCOUNT=@@ROWCOUNT
- IF @ROWCOUNT>0
- BEGIN
- INSERT INTO [dbo].[BACKUP_'+@TABLENAME+']
- SELECT @NEWID,''INSERT'',* FROM inserted
- END
- ELSE
- BEGIN
- INSERT INTO [dbo].[BACKUP_'+@TABLENAME+']
- SELECT @NEWID,''INSERT'',* FROM inserted
- SET @ROWCOUNT=@@ROWCOUNT
- END
- --==============================记录日志和回滚操作的SQL===========================
- --******************生成插入语句用到的列名(需避开自增字段)********************
- DECLARE @COLUMN1 NVARCHAR(MAX)=''''
- SELECT @COLUMN1+='',[''+COLUMN_NAME+'']'' FROM INFORMATION_SCHEMA.columns
- WHERE TABLE_NAME='''+@TABLENAME+'''
- AND COLUMNPROPERTY(OBJECT_ID('''+@TABLENAME+'''),COLUMN_NAME,''IsIdentity'')<>1 --非自增字段
- SET @COLUMN1=SUBSTRING(@COLUMN1,2,LEN(@COLUMN1))
- --*******************动态定义变量、删除条件匹配的列********************
- DECLARE @DECLARE VARCHAR(MAX)='''',@INTODECLARE VARCHAR(MAX)='''',@WHERE VARCHAR(MAX)='''',@COLUMN2 VARCHAR(MAX)=''''
- 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,
- @INTODECLARE+=''@''+COLUMN_NAME+'','',
- @COLUMN2+=''[''+COLUMN_NAME+''],'' ,
- @WHERE += ''ISNULL(''+ COLUMN_NAME+'','''''''')=ISNULL(@''+COLUMN_NAME+'','''''''') AND ''
- FROM INFORMATION_SCHEMA.columns
- WHERE TABLE_NAME='''+@TABLENAME+'''
- SET @DECLARE=LEFT(@DECLARE,LEN(@DECLARE)-1)
- SET @INTODECLARE=LEFT(@INTODECLARE,LEN(@INTODECLARE)-1)
- SET @COLUMN2=LEFT(@COLUMN2,LEN(@COLUMN2)-1)
- SET @WHERE= LEFT(@WHERE,LEN(@WHERE)-3)
- --*******************判断是否还原当前表的最近一次操作*******************
- DECLARE @SQL_ISLAST VARCHAR(MAX)=''
- SET NOCOUNT ON
- DECLARE @maxdate datetime
- SELECT @maxdate=max(updatetime) FROM UPDATE_LOG WHERE TableName='''''+@TABLENAME+'''''
- IF NOT EXISTS(SELECT 1 FROM UPDATE_LOG WHERE UpdateTime=@maxdate AND UPDATEGUID=''''''+@NEWID+'''''')
- BEGIN
- DECLARE @MAXGUID VARCHAR(50)
- SELECT @MAXGUID=UPDATEGUID FROM UPDATE_LOG WHERE UpdateTime=@maxdate
- PRINT ''''此操作并非最近一次操作,请逐步还原,此表最近一次操作的GUID是:''''+@MAXGUID
- RETURN
- END
- ''
- --********************还原insert和update操作用到的SQL*******************
- DECLARE @SQL_DELETE VARCHAR(MAX)=''
- SET ROWCOUNT 1 --设定相同条件下只删除1行
- DECLARE Cursor_ CURSOR FOR
- SELECT ''+@COLUMN2+'' FROM BACKUP_'+@TABLENAME+' WHERE UPDATEGUID= ''''''+@NEWID+'''''' AND UpdateType=''''INSERT''''
- OPEN Cursor_
- DECLARE ''+@DECLARE+''
- FETCH NEXT FROM Cursor_ INTO ''+@INTODECLARE+''
- WHILE @@FETCH_STATUS=0
- BEGIN
- DELETE FROM '+@TABLENAME+' WHERE ''+@WHERE+''
- FETCH NEXT FROM Cursor_ INTO ''+@INTODECLARE+''
- END
- CLOSE Cursor_
- DEALLOCATE Cursor_
- SET ROWCOUNT 0
- ''
- --*********************还原delete和update操作用到的SQL*******************
- DECLARE @SQL_INSERT VARCHAR(MAX)=''
- INSERT INTO '+@TABLENAME+' SELECT ''+@COLUMN1+'' FROM BACKUP_'+@TABLENAME+' WHERE UPDATEGUID=''''''+@NEWID+'''''' AND UpdateType=''''DELETE''''
- ''
- --*********************还原操作之后把备份表和log表的记录删掉*************
- DECLARE @SQL_DELGUID VARCHAR(MAX)=''
- DELETE FROM BACKUP_'+@TABLENAME+' WHERE UPDATEGUID IN(SELECT UPDATEGUID FROM UPDATE_LOG WHERE UpdateTime>=@maxdate AND TableName='''''+@TABLENAME+''''')
- DELETE FROM UPDATE_LOG WHERE UpdateTime>=@maxdate AND TableName='''''+@TABLENAME+'''''
- PRINT ''''回滚操作执行成功,共恢复 ''+CAST(@ROWCOUNT AS VARCHAR(10))+'' 条记录''''
- SET NOCOUNT OFF
- ''
- --*********************执行还原操作的SQL**********************************
- DECLARE @EXECSQL VARCHAR(500)=''
- DECLARE @SQL VARCHAR(MAX)
- SELECT @SQL=ROLLBACKSQL FROM UPDATE_LOG WHERE UPDATEGUID=''''''+@NEWID+''''''
- EXEC(@SQL)
- ''
- --==============================判断执行的哪种操作方式=================================
- DECLARE @DoType VARCHAR(MAX)=''UPDATE''
- IF NOT EXISTS(SELECT 1 FROM deleted)
- SET @DoType=''INSERT''
- IF NOT EXISTS(SELECT 1 FROM inserted)
- SET @DoType=''DELETE''
- IF NOT EXISTS(SELECT 1 FROM deleted) AND NOT EXISTS(SELECT 1 FROM inserted)
- RETURN
- IF @DoType=''UPDATE''
- BEGIN
- INSERT INTO [dbo].[UPDATE_LOG]
- SELECT @NEWID,GETDATE(),'''+@TABLENAME+''',''UPDATE'',@SQL_ISLAST+@SQL_DELETE+@SQL_INSERT+@SQL_DELGUID,@EXECSQL
- RETURN
- END
- IF @DoType=''DELETE''
- BEGIN
- INSERT INTO [dbo].[UPDATE_LOG]
- SELECT @NEWID,GETDATE(),'''+@TABLENAME+''',''DELETE'',@SQL_ISLAST+@SQL_INSERT+@SQL_DELGUID,@EXECSQL
- RETURN
- END
- IF @DoType=''INSERT''
- BEGIN
- INSERT INTO [dbo].[UPDATE_LOG]
- SELECT @NEWID,GETDATE(),'''+@TABLENAME+''',''INSERT'',@SQL_ISLAST+@SQL_DELETE+@SQL_DELGUID,@EXECSQL
- RETURN
- END
- END
- '
- EXEC (@SQLTR)
- END
- END
运行这段代码,你会创建一个存储过程,下面来建一个测试表简单测一下这个存储过程的功能吧:
- CREATE TABLE test(
- [id] [int] NULL,
- [name] [varchar](10) NULL
- )
- INSERT INTO test
- SELECT 1,'a'
- UNION ALL
- SELECT 2,'b'
- UNION ALL
- SELECT 3,'c'
- UNION ALL
- SELECT 4,'d'
- UNION ALL
- SELECT 5,'a'
- UNION ALL
- SELECT 6,'b'
- SELECT * FROM test
检查一下,表建好了:
接着执行存储过程给test表添加回滚日志:
- EXEC SP_UPDATE_LOG 'test' --给test表建立update回滚日志
- SELECT * FROM [dbo].[BACKUP_test] --test表数据备份
- 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)的更多相关文章
- MySQL中的重做日志(redo log),回滚日志(undo log),以及二进制日志(binlog)的简单总结
MySQL中有六种日志文件,分别是:重做日志(redo log).回滚日志(undo log).二进制日志(binlog).错误日志(errorlog).慢查询日志(slow query log).一 ...
- python logging 实现的进程安全的文件回滚日志类
python标准库中的logging模块在记录日志时经常会用到,但在实际使用发现它自带的用于本地日志回滚的类 logging.handlers.RotatingFileHandler 在多进程环境下会 ...
- mysql回滚日志
一.回滚日志(undo log) 1.作用 保存了事务发生之前的数据的一个版本,可以用于回滚,同时可以提供多版本并发控制下的读(MVCC),也即非锁定读 2.内容 逻辑格式的日志,在执行undo的时候 ...
- Log4net入门(回滚日志文件篇)
在上一篇Log4net(日志文件篇)中,我们使用"log4net.Appender.FileAppender"将日志信息输出到一个单一的文件中,随着应用程序的持续使用,该日志文件会 ...
- sqlserver事务与回滚
如果要在Production执行数据改动必须小心,可以使用事务提前验证一下自己写的SQL是不是你期望的.尤其是Update的where 条件有问题的话,跟新的记录就会超出预期的范围.如下面的语句,一着 ...
- sprint test 添加事务回滚机制
1.原因: 单元测试的时候频繁操作数据库需要修改很多数据,造成不必要的操作,添加事务之后就可以重复对一条数据进行操作,并且在返回结果后进行回滚. 2.解决: 原先继承的是 AbstractJUnit ...
- Quartz:Quartz添加事务回滚报错
自动任务类: @PersistJobDataAfterExecution @DisallowConcurrentExecution public class ReCodeBack implements ...
- 两表关联更新,用于update 回滚
create table test1 as select * from dba_objects; create table test2 as select * from dba_objects; cr ...
- SQLite中的WAL机制详细介绍-与回滚日志原理
一.什么是WAL? WAL的全称是Write Ahead Logging,它是很多数据库中用于实现原子事务的一种机制,SQLite在3.7.0版本引入了该特性. 二.WAL如何工作? 在引入WAL机制 ...
随机推荐
- canvas 绘制图像
结果: 代码: <!DOCTYPE html> <html> <head lang="en"> <meta charset="U ...
- Django 碎片集合
命令行创建Django项目 熟记建立django命令:django-admin startproject xx (start project) 目录介绍 manage.py 文件是用来管理文件 ...
- MyBatis典型的错误org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)
XXXmapper.java(接口) XXXmapper.xml(结果集映射) //此两个文件要在统一包下,且xml中的namespace是唯一的,为了区分须写成 该xml的全路径
- c printf打印格式
关于小数点位数的举例: <pre lang="c" escaped="true">#include <stdio.h> /* 当fah ...
- linux C使用strerror来追查错误信息
最近工作中有个需求:程序将文件进行处理,然后将处理完毕的文件挪走.我用了rename函数来挪动文件,可是在docker化的环境中,文件却无法挪动.不知道什么原因.现在,对程序进行调整,如果rename ...
- linux之文件操作
1. 文件操作思维导图 2. linux系统目录结构及简单说明 linux目录图: root --- 启动Linux时使用的一些核心文件.如操作系统内核.引导程序Grub等. home --- 存储普 ...
- Chrome 的书签太多如何分类整理比较好
对于Chrome书签太多,如何进行分类整理,下面给出几种方法~ 工具/原料 电脑 Chrome浏览器 delicious.Diigo等 方法/步骤1 1.把书签中常用网站只保存LOGO,放在书签栏最前 ...
- idea操作
archetypeCatalog internal 1字体: 2编码 http://blog.csdn.net/frankcheng5143/article/details/50779149 3部署 ...
- JGroups 入门实践
前言 JGroups是一个开源的纯java编写的可靠的群组通讯工具.其工作模式基于IP多播,但可以在可靠性和群组成员管理上进行扩展.其结构上设计灵活,提供了一种灵活兼容多种协议的协议栈. JGroup ...
- 数据流重定向和管道命令, grep, tr,sort, wc, cut,split,tee,sleep(shell 02)
主要内容 1.标准输入输出和错误 2.管道命令和 grep, tr,sort, wc, cut,split,tee,sleep 标准输入输出和错误 标准输入(stdin) 是指令数据的输入,代码为0, ...