1. 程式人生 > >SQLServer 自定義回滾表(update/delete/insert)並實行回滾

SQLServer 自定義回滾表(update/delete/insert)並實行回滾

實現update操作的回滾日誌的建立。首先要注意的是:這裡的update並不只是表的update操作,它包含delete和insert操作!

下面直接上程式碼(copy到你的資料庫裡面直接就可以執行):

[sql] view plain copy print?
  1. CREATEPROCEDURE [dbo].[SP_UPDATE_LOG]  
  2.     @TABLENAME VARCHAR(50)  
  3. AS
  4. BEGIN
  5.     SET NOCOUNT ON;  
  6.     IF NOT EXISTS(SELECT * FROM sys.tables WHERENAME = @TABLENAME AND TYPE = 
    ‘U’ )  
  7.     BEGIN
  8.         PRINT’ERROR:not exist table ’[email protected]  
  9.         RETURN
  10.     END
  11.     IF (@TABLENAME LIKE‘BACKUP_%’OR @TABLENAME=‘UPDATE_LOG’ )  
  12.     BEGIN
  13.         –PRINT’ERROR:not exist table ’[email protected]
  14.         RETURN
  15.     END
  16.     –================================判斷是否存在 UPDATE_LOG 表============================
  17.     IF NOT EXISTS(SELECT * FROM sys.tables WHERENAME = ‘UPDATE_LOG’AND TYPE = ‘U’)  
  18.         CREATETABLE 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 WHERENAME = ‘BACKUP_’[email protected] AND TYPE = ‘U’)  
  29.     BEGIN
  30.         DECLARE test_Cursor CURSORFOR
  31.         SELECT COLUMN_NAME,DATA_TYPE,CHARACTER_MAXIMUM_LENGTH FROM INFORMATION_SCHEMA.columns  
  32.         WHERE [email protected]  
  33.         OPEN test_Cursor  
  34.         DECLARE @SQLTB NVARCHAR(MAX)=
  35.         DECLARE @COLUMN_NAME NVARCHAR(50),@DATA_TYPE VARCHAR(20),@CHARACTER_MAXIMUM_LENGTH INT
  36.         FETCHNEXTFROM test_Cursor INTO @COLUMN_NAME,@DATA_TYPE,@CHARACTER_MAXIMUM_LENGTH  
  37.         WHILE @@FETCH_STATUS=0  
  38.         BEGIN
  39.             SET @[email protected]+‘[‘[email protected]_NAME+‘] ’[email protected]_TYPE+CASEISNULL(@CHARACTER_MAXIMUM_LENGTH,0) WHEN 0 THENWHEN -1 THEN‘(MAX)’ELSE‘(‘+CAST(@CHARACTER_MAXIMUM_LENGTH ASVARCHAR(10))+‘)’END+‘,’
  40.             FETCHNEXTFROM test_Cursor INTO @COLUMN_NAME,@DATA_TYPE,@CHARACTER_MAXIMUM_LENGTH  
  41.         END
  42.         SET @SQLTB=‘CREATE TABLE BACKUP_’[email protected]+‘ (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 WHERENAME = ‘tg_’[email protected]+‘_Update’AND TYPE = ‘TR’)  
  49.     BEGIN
  50.         DECLARE @SQLTR NVARCHAR(MAX)  
  51.         SET @SQLTR=’  
  52. CREATETRIGGER tg_[email protected]+’_Update  
  53.     ON[email protected]+’
  54.     AFTERUpdate,Delete,Insert
  55. AS
  56. BEGIN
  57.     SET NOCOUNT ON;  
  58.     –==============================獲取GUID==========================================
  59.     DECLARE @NEWID VARCHAR(36)=NEWID()  
  60.     –===========================將刪掉或新增的資料插入備份表=========================
  61.     DECLARE @ROWCOUNT INT
  62.     INSERTINTO [dbo].[BACKUP_[email protected]+’]  
  63.     SELECT @NEWID,DELETE,* FROM deleted  
  64.     SET @[email protected]@ROWCOUNT  
  65.     IF @ROWCOUNT>0  
  66.     BEGIN
  67.         INSERTINTO [dbo].[BACKUP_[email protected]+’]  
  68.         SELECT @NEWID,INSERT,* FROM inserted  
  69.     END
  70.     ELSE
  71.     BEGIN
  72.         INSERTINTO [dbo].[BACKUP_[email protected]+’]  
  73.         SELECT @NEWID,INSERT,* FROM inserted  
  74.         SET @[email protected]@ROWCOUNT  
  75.     END
  76.     –==============================記錄日誌和回滾操作的SQL===========================
  77.     –******************生成插入語句用到的列名(需避開自增欄位)********************
  78.     DECLARE @COLUMN1 NVARCHAR(MAX)=
  79.     SELECT @COLUMN1+=,[+COLUMN_NAME+]FROM INFORMATION_SCHEMA.columns  
  80.     WHERE TABLE_NAME=[email protected]+’
  81.     AND COLUMNPROPERTY(OBJECT_ID([email protected]+’),COLUMN_NAME,IsIdentity)<>1 –非自增欄位
  82.     SET @COLUMN1=SUBSTRING(@COLUMN1,2,LEN(@COLUMN1))  
  83.     –*******************動態定義變數、刪除條件匹配的列********************
  84.     DECLARE @DECLAREVARCHAR(MAX)=,@INTODECLARE VARCHAR(MAX)=,@WHEREVARCHAR(MAX)=,@COLUMN2 VARCHAR(MAX)=
  85.     SELECT @DECLARE+=@+COLUMN_NAME++DATA_TYPE+CASEISNULL(CAST(CHARACTER_OCTET_LENGTH ASVARCHAR(10)),WHENTHEN,WHEN-1THEN(MAX),ELSE(+CAST(CHARACTER_OCTET_LENGTH ASVARCHAR(10))+),END,  
  86.         @INTODECLARE+=@+COLUMN_NAME+,,  
  87.         @COLUMN2+=[+COLUMN_NAME+], ,  
  88.         @WHERE += ISNULL(+ COLUMN_NAME+,)=ISNULL(@+COLUMN_NAME+,AND
  89.     FROM INFORMATION_SCHEMA.columns  
  90.     WHERE TABLE_NAME=[email protected]+’
  91.     SET @DECLARE=LEFT(@DECLARE,LEN(@DECLARE)-1)  
  92.     SET @INTODECLARE=LEFT(@INTODECLARE,LEN(@INTODECLARE)-1)  
  93.     SET @COLUMN2=LEFT(@COLUMN2,LEN(@COLUMN2)-1)  
  94.     SET @WHERELEFT(@WHERE,LEN(@WHERE)-3)  
  95.     –*******************判斷是否還原當前表的最近一次操作*******************         
  96.     DECLARE @SQL_ISLAST VARCHAR(MAX)=
  97.     SET NOCOUNT ON
  98.     DECLARE @maxdate datetime  
  99.     SELECT @maxdate=max(updatetime) FROM UPDATE_LOG WHERE TableName=[email protected]+’
  100.     IF NOT EXISTS(SELECT 1 FROM UPDATE_LOG WHERE [email protected] AND UPDATEGUID=[email protected]+)  
  101.     BEGIN
  102.         DECLARE @MAXGUID VARCHAR(50)  
  103.         SELECT @MAXGUID=UPDATEGUID FROM UPDATE_LOG WHERE [email protected]  
  104.         PRINT 此操作並非最近一次操作,請逐步還原,此表最近一次操作的GUID是:[email protected]  
  105.         RETURN
  106.     END
  107.     
  108.     –********************還原insert和update操作用到的SQL*******************
  109.     DECLARE @SQL_DELETE VARCHAR(MAX)=
  110.     SET ROWCOUNT 1  –設定相同條件下只刪除1行        
  111.     DECLARE Cursor_ CURSORFOR
  112.     SELECT[email protected]+FROM BACKUP_[email protected]+’WHERE UPDATEGUID= [email protected]+AND UpdateType=INSERT
  113.     OPEN Cursor_  
  114.     DECLARE[email protected]DECLARE+
  115.     FETCHNEXTFROM Cursor_ INTO[email protected]+
  116.     WHILE @@FETCH_STATUS=0  
  117.     BEGIN
  118.         DELETEFROM[email protected]+’WHERE[email protected]WHERE+
  119.         FETCHNEXTFROM Cursor_ INTO[email protected]+
  120.     END
  121.     CLOSE Cursor_  
  122.     DEALLOCATE Cursor_  
  123.     SET ROWCOUNT 0  
  124.     
  125.     –*********************還原delete和update操作用到的SQL*******************
  126.     DECLARE @SQL_INSERT VARCHAR(MAX)=
  127.     INSERTINTO[email protected]+’SELECT[email protected]+FROM BACKUP_[email protected]+’WHERE UPDATEGUID=[email protected]+AND UpdateType=DELETE
  128.     
  129.     –*********************還原操作之後把備份表和log表的記錄刪掉*************
  130.     DECLARE @SQL_DELGUID VARCHAR(MAX)=
  131.     DELETEFROM BACKUP_[email protected]+’WHERE  UPDATEGUID IN(SELECT UPDATEGUID FROM UPDATE_LOG WHERE UpdateTime>[email protected] AND TableName=[email protected]+’)  
  132.     DELETEFROM UPDATE_LOG WHERE UpdateTime>[email protected] AND TableName=[email protected]+’
  133.     PRINT 回滾操作執行成功,共恢復 +CAST(@ROWCOUNT ASVARCHAR(10))+ 條記錄
  134.     SET NOCOUNT OFF
  135.     
  136.     –*********************執行還原操作的SQL**********************************
  137.     DECLARE @EXECSQL VARCHAR(500)=
  138.     DECLARE @SQL VARCHAR(MAX)  
  139.     SELECT @SQL=ROLLBACKSQL FROM UPDATE_LOG WHERE UPDATEGUID=[email protected]+
  140.     EXEC(@SQL)   
  141.     
  142.     –==============================判斷執行的哪種操作方式=================================
  143.     DECLARE @DoType VARCHAR(MAX)=UPDATE
  144.     IF NOT EXISTS(SELECT 1 FROM deleted)  
  145.         SET @DoType=INSERT
  146.     IF NOT EXISTS(SELECT 1 FROM inserted)  
  147.         SET @DoType=DELETE
  148.     IF NOT EXISTS(SELECT 1 FROM deleted) ANDNOT EXISTS(SELECT 1 FROM inserted)  
  149.         RETURN
  150.     IF @DoType=UPDATE
  151.     BEGIN
  152.         INSERTINTO [dbo].[UPDATE_LOG]  
  153.         SELECT @NEWID,GETDATE(),[email protected]+’,UPDATE,@[email protected][email protected][email protected]_DELGUID,@EXECSQL  
  154.         RETURN
  155.     END
  156.     IF @DoType=DELETE
  157.     BEGIN
  158.         INSERTINTO [dbo].[UPDATE_LOG]  
  159.         SELECT @NEWID,GETDATE(),[email protected]+’,DELETE,@[email protected][email protected]_DELGUID,@EXECSQL  
  160.         RETURN
  161.     END
  162.     IF @DoType=INSERT
  163.     BEGIN
  164.         INSERTINTO [dbo].[UPDATE_LOG]  
  165.         SELECT @NEWID,GETDATE(),[email protected]+’,INSERT,@[email protected][email protected]_DELGUID,@EXECSQL  
  166.         RETURN
  167.     END
  168. END
  169.             ’  
  170.         EXEC (@SQLTR)  
  171.     END
  172. END
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 '[email protected]
        RETURN
    END
    IF (@TABLENAME LIKE'BACKUP_%' OR @TABLENAME='UPDATE_LOG' )
    BEGIN
        --PRINT'ERROR:not exist table '[email protected]
        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_'[email protected] AND TYPE = 'U')
    BEGIN
        DECLARE test_Cursor CURSOR FOR
        SELECT COLUMN_NAME,DATA_TYPE,CHARACTER_MAXIMUM_LENGTH FROM INFORMATION_SCHEMA.columns
        WHERE [email protected]
        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 @[email protected]+'['[email protected]_NAME+'] '[email protected]_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_'[email protected]+' (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_'[email protected]+'_Update' AND TYPE = 'TR')
    BEGIN
        DECLARE @SQLTR NVARCHAR(MAX)
        SET @SQLTR='
CREATE TRIGGER tg_'[email protected]+'_Update
    ON  '[email protected]+'
    AFTER Update,Delete,Insert
AS
BEGIN 
    SET NOCOUNT ON;
    --==============================獲取GUID==========================================
    DECLARE @NEWID VARCHAR(36)=NEWID()

    --===========================將刪掉或新增的資料插入備份表=========================
    DECLARE @ROWCOUNT INT
    INSERT INTO [dbo].[BACKUP_'[email protected]+']
    SELECT @NEWID,''DELETE'',* FROM deleted
    SET @[email protected]@ROWCOUNT
    IF @ROWCOUNT>0
    BEGIN
        INSERT INTO [dbo].[BACKUP_'[email protected]+']
        SELECT @NEWID,''INSERT'',* FROM inserted
    END
    ELSE
    BEGIN
        INSERT INTO [dbo].[BACKUP_'[email protected]+']
        SELECT @NEWID,''INSERT'',* FROM inserted
        SET @[email protected]@ROWCOUNT
    END

    --==============================記錄日誌和回滾操作的SQL===========================


    --******************生成插入語句用到的列名(需避開自增欄位)********************
    DECLARE @COLUMN1 NVARCHAR(MAX)=''''
    SELECT @COLUMN1+='',[''+COLUMN_NAME+'']'' FROM INFORMATION_SCHEMA.columns
    WHERE TABLE_NAME='''[email protected]+'''
    AND COLUMNPROPERTY(OBJECT_ID('''[email protected]+'''),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='''[email protected]+'''
    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='''''[email protected]+'''''
    IF NOT EXISTS(SELECT 1 FROM UPDATE_LOG WHERE [email protected] AND UPDATEGUID=''''''[email protected]+'''''')
    BEGIN
        DECLARE @MAXGUID VARCHAR(50)
        SELECT @MAXGUID=UPDATEGUID FROM UPDATE_LOG WHERE [email protected]
        PRINT ''''此操作並非最近一次操作,請逐步還原,此表最近一次操作的GUID是:''''[email protected]
        RETURN
    END
    ''

    --********************還原insert和update操作用到的SQL*******************

    DECLARE @SQL_DELETE VARCHAR(MAX)=''
    SET ROWCOUNT 1  --設定相同條件下只刪除1行        
    DECLARE Cursor_ CURSOR FOR
    SELECT ''[email protected]+'' FROM BACKUP_'[email protected]+' WHERE UPDATEGUID= ''''''[email protected]+'''''' AND UpdateType=''''INSERT''''
    OPEN Cursor_
    DECLARE ''[email protected]+''
    FETCH NEXT FROM Cursor_ INTO ''[email protected]+''
    WHILE @@FETCH_STATUS=0
    BEGIN                  
        DELETE FROM '[email protected]+' WHERE ''[email protected]+''
        FETCH NEXT FROM Cursor_ INTO ''[email protected]+''
    END
    CLOSE Cursor_
    DEALLOCATE Cursor_
    SET ROWCOUNT 0
    ''

    --*********************還原delete和update操作用到的SQL*******************

    DECLARE @SQL_INSERT VARCHAR(MAX)=''
    INSERT INTO '[email protected]+' SELECT ''[email protected]+'' FROM BACKUP_'[email protected]+' WHERE UPDATEGUID=''''''[email protected]+'''''' AND UpdateType=''''DELETE''''
    ''

    --*********************還原操作之後把備份表和log表的記錄刪掉*************

    DECLARE @SQL_DELGUID VARCHAR(MAX)=''
    DELETE FROM BACKUP_'[email protected]+' WHERE  UPDATEGUID IN(SELECT UPDATEGUID FROM UPDATE_LOG WHERE UpdateTime>[email protected] AND TableName='''''[email protected]+''''')
    DELETE FROM UPDATE_LOG WHERE UpdateTime>[email protected] AND TableName='''''[email protected]+'''''
    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=''''''[email protected]+''''''  
    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(),'''[email protected]+''',''UPDATE'',@[email protected][email protected][email protected]_DELGUID,@EXECSQL
        RETURN
    END
    IF @DoType=''DELETE''
    BEGIN
        INSERT INTO [dbo].[UPDATE_LOG]
        SELECT @NEWID,GETDATE(),'''[email protected]+''',''DELETE'',@[email protected][email protected]_DELGUID,@EXECSQL
        RETURN
    END
    IF @DoType=''INSERT''
    BEGIN
        INSERT INTO [dbo].[UPDATE_LOG]
        SELECT @NEWID,GETDATE(),'''[email protected]+''',''INSERT'',@[email protected][email protected]_DELGUID,@EXECSQL
        RETURN
    END
END
            '
        EXEC (@SQLTR)
    END
END
 

執行這段程式碼,你會建立一個儲存過程,下面來建一個測試表簡單測一下這個儲存過程的功能吧:

[sql] view plain copy print?
  1. CREATETABLE test(  
  2.     [id] [intNULL,  
  3.     [name] [varchar](10) NULL
  4. )   
  5. INSERTINTO test  
  6. SELECT 1,‘a’
  7. UNIONALL
  8. SELECT 2,‘b’
  9. UNIONALL
  10. SELECT 3,‘c’
  11. UNIONALL
  12. SELECT 4,‘d’
  13. UNIONALL
  14. SELECT 5,‘a’
  15. UNIONALL
  16. SELECT 6,‘b’
  17. SELECT * FROM test  
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表添加回滾日誌:

[sql] view plain copy print?
  1. EXEC SP_UPDATE_LOG ‘test’–給test表建立update回滾日誌
  2. SELECT * FROM [dbo].[BACKUP_test]   –test表資料備份
  3. SELECT * FROM [dbo].[UPDATE_LOG]    –update操作記錄
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語句執行,注意要先執行時間最近的操作記錄,一步一步還原:

還原一步之後變成了這個樣子,下面再還原一步:

到目前為止看下完全還原了吧~

PS:執行delete和insert操作也是一樣的回滾步驟

相關推薦

SQLServer 定義(update/delete/insert)實行

實現update操作的回滾日誌的建立。首先要注意的是:這裡的update並不只是表的update操作,它包含delete和insert操作! 下面直接上程式碼(copy到你的資料庫裡面直接就可以執行): [sql] view plain copy print?CREATEPROCEDURE [dbo].[

sqlserver定義函式(標量值函式,值函式)

在sqlserver中,根據函式返回值形式的不同將使用者自定義的函式分為”標量值函式“和”表值函式“兩種型別。如下圖所示,可以在資料庫中的可程式設計->函式中進行檢視。 標量函式,返回單個值,表值函式返回表資料 1.標量值函式 標量值函式返回一個確定型別的標量值,其

SqlServer定義函數Function中調用with as

log nes 此外 arc targe -- ans 如果 ransac SET QUOTED_IDENTIFIER ON 標識符可以由雙引號分隔,而文字必須由單引號分隔 SET QUOTED_IDENTIFIER OFF 標識符不可加引

Django定義用戶替換默認用戶認證

django1、自定義用戶表from django.db import models from django.contrib.auth.models import AbstractUser # Create your models here. class UserProfile(AbstractUser):

sqlserver定義函數

ssi sch 及其 insert 語句 關鍵字 sql varchar join 標量函數 RETURNS 子句指定一種標量數據類型,則函數為標量值函數。 語法 Create function 函數名(參數) Returns 返回值數據類型 [with {Encrypt

django定義用戶

address ati port ons 手機號碼 創建 clas ret pan django中已經給我生成默認的User表,其中的字段已經可以滿足我們的日常需求。 但有時候,我們需要更多的字段,我們就可以自定義。來替換django自帶的User表。 #models.

dedecms sql 定義模型 多查詢

 副表:wl_addon18,wl_addon17; 主表:wl_archives {dede:sql sql="select wl_archives.*,wl_archives.litpic, wl_archives.title,wl_addon18.style,wl_addon

定義乘法口訣

#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> void cfkjb(int x) { for (int i = 1; i <= x; i++) { for (int j =

可以定義有關的單的驗證,通過重寫authenticate,從可以通過郵箱驗證(原來只能通過使用者名稱驗證)

在settings.py中加入 AUTHENTICATION_BACKENDS = ( 'users.views.CustomBackend', ) 其中users代表是app,CustomBackend代表的是類名 2. 在views.py中寫入

sqlserver定義摺疊符號

官方沒有這個功能!  是不是很不人性化,  類似 VS  #region  #endregion 的功能很方便 ,  不過機智的寶寶想到一個黑科技可以實現此功能 .  很簡單 就是 ;  begin  --  end    適用於非功能性sql語句 ,  比如好多查詢的sql

Sqlserver定義函式Function

https://www.cnblogs.com/weihengblogs/p/4216755.html 一.FUNCTION: 在sqlserver2008中有3中自定義函式:標量函式/內聯表值函式/多語句表值函式,首先總結下他們語法的異同點: 同點:1.建立定義是一樣的:  &nbs

SharePoint Online/2019 定義Mordern UI

  前言   用過SharePoint Online或者Server 2019版本的朋友們,應該很熟悉SharePoint為我們帶來的Morden UI,而這個頁面的訂製,也跟原來的表單定製方式不同了,而且更加簡單了。   而且,試了一下,之前在NewForm頁新增JavaScript或者JQuery的方

SQLServer --定義帶輸入引數的儲存過程

帶輸入引數的儲存過程 在引數中新增預設值 use StuManageDB go if exists(select * from sysobjects where name=

SQLServer -- 定義無引數儲存過程

自定義儲過程 use StuManageDB go if exists(Select * from sysobjects where name='usp_ScoreQuery') drop pro

Java 定義雙向鏈

fir pack 連續 null 定位 .net 單鏈表 提高 https 雙向鏈表 LinkedList其實也就是我們在數據結構中的鏈表,這種數據結構有這樣的特性: 分配內存空間不是必須是連續的;插入、刪除操作很快,只要修改前後指針就OK了,時間復雜度為O(1);訪問

Android 定義AlertDialog提交

Android在前端的設計中,有些時候我們需要提交一些資料或表單的時候,由於資料量不是很大,我們沒有必要建立一個全新的Activity來提交很少的資料,這樣會顯得很單調,也浪費資源,其實不只是Android中這樣,網頁設計也會大量利用彈窗式對話方塊來提交一些資料。這個時候我

sqlserver 定義、刪除、執行 函式和儲存過程

自定義儲存過程: if (object_id('p_wm_get_dynstore', 'P') is not null) drop proc p_wm_get_dynstore go create procedure p_wm_get_dynstore( @s

SqlServer定義數據類型

系統數據 分享圖片 必須 創建 自定義數據類型 server 分享 添加 sql 定義:用戶自己設計並實現的數據類型就稱為用戶自定義數據類型,即使這些數據類型基於系統數據類型。 創建用戶自定義數據類型時,必須提供三個數:   數據類型的名稱   所基於的系統數據

定義 Android 鐘盤,這一篇就夠了

關於本文:本文原先在我的 CSDN 部落格釋出(由圖片水印能發現),整理以往部落格過程中,發現當時總結的很仔細,所以將其遷移到這裡,希望對大家在自定義 View 方面,能有所幫助

(七十六)c#Winform定義控制元件-單驗證元件

前提 入行已經7,8年了,一直想做一套漂亮點的自定義控制元件,於是就有了本系列文章。 GitHub:https://github.com/kwwwvagaa/NetWinformControl 碼雲:https://gitee.com/kwwwvagaa/net_winform_custom_control.