SQL SERVER回滚恢复误操作的数据
在生产数据库做CURD操作时,可能会有执行某条语句误操作的情况发生,针对这个种情况有两点建议:
1、 在SQL SERVER上开启事务确认功能,当执行完语句后确认无误,再提交事务。(开启方法见附件图片)。
2、 新建存储过程,粘贴附件脚本。此存储过程执行后能够自动产生两个操作日志表,自动记录CRUD的所有操作。适用于提交事务后才发现错误的情况。只需要打开表UPDATE_LOG,粘贴RollbackupSQL里的语句执行即可恢复数据。
注意:1)如果表中有自增长的ID,所恢复数据的ID值是最大ID+1。
2)由于正常操作也会回写操作日志,注意及时清理日志表,或者在执行完后删掉新建的存储过程、触发器及表。
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
SQL SERVER回滚恢复误操作的数据的更多相关文章
- SQL server基础知识(表操作、数据约束、多表链接查询)
SQL server基础知识 一.基础知识 (1).存储结构:数据库->表->数据 (2).管理数据库 增加:create database 数据库名称 删除:drop database ...
- Git误操作 git reset强制回滚 恢复commit方法
参考: 找回Git中丢失的Commit Git误操作 git reset强制回滚 恢复commit方法 使用Git时,常有误操作,在Commit之后又执行了git reset --hard HEAD强 ...
- 转 一篇关于sql server 三种恢复模式的文章,从sql server 的机制上来写的,感觉很不错,转了
简介 SQL Server中的事务日志无疑是SQL Server中最重要的部分之一.因为SQL SERVER利用事务日志来确保持久性(Durability)和事务回滚(Rollback).从而还部分确 ...
- SQL Server的实例恢复解析
同Oracle一样,SQL Server在非一致性关闭的时候也会进行实例恢复(Instance Recovery),本文根据stack overflow的文章介绍一些SQL Server实例恢复的知识 ...
- 【转】mysql触发器的实战(触发器执行失败,sql会回滚吗)
1 引言Mysql的触发器和存储过程一样,都是嵌入到mysql的一段程序.触发器是mysql5新增的功能,目前线上凤巢系统.北斗系统以及哥伦布系统使用的数据库均是mysql5.0.45版本,很多程 ...
- SQL Server数据库有三种恢复模式:简单恢复模式、完整恢复模式和大容量日志恢复模式
SQL Server数据库有三种恢复模式:简单恢复模式.完整恢复模式和大容量日志恢复模式: 1.Simple 简单恢复模式, Simple模式的旧称叫”Checkpoint with truncate ...
- [SQL Server]数据库的恢复
数据库恢复是和数据库备份相对应的操作,它是将数据库备份重新加载到系统中的过程.数据库恢复可以创建备份完成时数据库中存在的相关文件,但是备份以后的所有数据库修改都将丢失. SQL Server进行数据库 ...
- SQL Server 索引的图形界面操作 <第十二篇>
一.索引的图形界面操作 SQL Server非常强大的就是图形界面操作.关于索引方面也一样那么强大,很多操作比如说重建索引啊,查看各种统计信息啊,都能够通过图形界面快速查看和操作,下面来看看SQL S ...
- 从SQL Server到MySQL,近百亿数据量迁移实战
从SQL Server到MySQL,近百亿数据量迁移实战 狄敬超(3D) 2018-05-29 10:52:48 212 沪江成立于 2001 年,作为较早期的教育学习网站,当时技术选型范围并不大:J ...
随机推荐
- 转载:二次指数平滑法求预测值的Java代码
原文地址: http://blog.csdn.net/qustmeng/article/details/52186378?locationNum=4&fps=1 import java.uti ...
- 记一次MSSQL到MySQL大数据迁移过程
工作中遇到一个需求 要将MSSQL数据库中共计12张表的数据大概1000W行数据迁移到MySQL数据库中,去年另一个同事负责这件事情,他采用的方法是先将MSSQL数据库里的数据生成同MySQL数据库表 ...
- [LeetCode&Python] Problem 496. Next Greater Element I
You are given two arrays (without duplicates) nums1 and nums2 where nums1’s elements are subset of n ...
- API设计风格(RRC、REST、GraphQL、服务端驱动)
API设计风格(RRC.REST.GraphQL.服务端驱动) Web API设计其实是一个挺重要的设计话题,许多公司都会有公司层面的Web API设计规范,几乎所有的项目在详细设计阶段都会进行API ...
- 获取js 文件传递的参数并使用json2进行json数据转换
主要的技术就不用详细进行介绍了,就是使用js文件进行参数的传递,用途有一下几个: 1,进行js的版本控制. 2,获取参数并,进行一些额外功能的添加(比如使用js 进行用户验证,设计开发API (一些开 ...
- No result defined for action com.nynt.action.ManageAction and result input问题
No result defined for action com.nynt.action.ManageAction and result input 问题原因: 1). 在action类中定义的一个r ...
- leetcode -day30 Reverse Linked List II
1. Reverse Linked List II Reverse a linked list from position m to n. Do it in-place and in one- ...
- Java中 @Override 的作用
@Override是伪代码,表示重写(当然不写也可以),不过写上有如下好处: 可以当注释用,方便阅读: 编译器可以给你验证@Override下面的方法名是否是你父类中所有的,如果没有则报错.例如,你如 ...
- bzoj 4772 显而易见的数论——拆分数(五边形数定理)+线性筛
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4772 题解:https://blog.csdn.net/Dream_Lolita/artic ...
- TP5 首页导航一级和二级分类
<ul id="jsddm"> <li><a class="navi_home" href="{:url('/index ...