1. 程式人生 > >SQL Server DDL Triggers to Track All Database Changes(用SQLServer DDL 觸發器跟蹤所有資料庫變化)

SQL Server DDL Triggers to Track All Database Changes(用SQLServer DDL 觸發器跟蹤所有資料庫變化)

Problem

In a perfect world, only the DBA would have sa privileges, F5 would only ever be hit on purpose, every change would go through rigorous source control procedures, and we would have full backups of all databases every minute. Of course, in reality, we deal with much different circumstances, and we can find ourselves (or overhear someone else) saying, "Oops... how do I fix that?" One of the more common scenarios I've seen involves someone editing a stored procedure multiple times between backups or within some kind of cycle, and then wishing they had version (current - 1) available. It's not in the backup yet, so can't be restored; and the user, of course, has closed his or her window without saving.

Solution

There are a lot of solutions to this issue, of course. They include tightening down server access, adopting a reliable source control system, and implementing a rigorous and well-documented deployment process. These things do not happen overnight, so in the meantime, DDL Triggers can provide a short-term fix that is both easy to implement and simple to manage. The approach is to take a snapshot of the current objects in the database, and then log all DDL changes from that point forward. With a well-managed log, you could easily see the state of an object at any point in time (assuming, of course, the objects are not encrypted).

So where do we start? First, I like to keep housekeeping items (monitoring, administration etc.) in their own database. This allows me to query things centrally and also to control growth separately. For this task, let's use a database called AuditDB:

CREATE DATABASE AuditDB;
GO

To keep things relatively simple, let's assume we are only interested in actions taken on stored procedures - create, alter, drop. We have a set of stored procedures already, and they are in a given state. We will need to capture that state, in addition to any changes that are made to them from that point forward. This way, we will always be able to get back to any state, including the original state.

In addition to the data specific to the actions taken on stored procedures, we can also think of several other pieces of information we would want to store about each event. For example:

  • database name
  • schema / object name
  • login information
  • host name / IP address (useful with SQL auth)

So here is the definition for a table to capture these events and the surrounding information about them:

USE AuditDB;
GO


CREATE TABLE dbo.DDLEvents
(
    EventDate    DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    EventType    NVARCHAR(64),
    EventDDL     NVARCHAR(MAX),
    EventXML     XML,
    DatabaseName NVARCHAR(255),
    SchemaName   NVARCHAR(255),
    ObjectName   NVARCHAR(255),
    HostName     VARCHAR(64),
    IPAddress    VARCHAR(48),
    ProgramName  NVARCHAR(255),
    LoginName    NVARCHAR(255)
);

Yes, we could keep the table skinnier and use [object_id] instead of schema/object name, also protecting us from resolution problems due to renames. However, often stored procedures are dropped and re-created, in which case the system will generate a new [object_id]. I also prefer to use the database name to make ad hoc queries (and script automation) against specific databases easier. You can choose which metadata to rely on; personally, I'll trade the space for readability and scriptability.

Now that the table exists, we can easily grab a snapshot of our existing stored procedure definitions, leaving out some of the irrelevant auditing data, as follows (replacing 'my name' with whatever you want to display for the initial rows):

USE YourDatabase;
GO


INSERT AuditDB.dbo.DDLEvents
(
    EventType,
    EventDDL,
    DatabaseName,
    SchemaName,
    ObjectName,
    LoginName
)
SELECT
    'CREATE_PROCEDURE',
    OBJECT_DEFINITION([object_id]),
    DB_NAME(),
    OBJECT_SCHEMA_NAME([object_id]),
    OBJECT_NAME([object_id]),
    'my name'
FROM
    sys.procedures;

Now we're ready to start capturing actual changes to these procedures as they happen. You can create a DDL Trigger with the following code, that will record pertinent data to the above table when changes are made to stored procedures:

USE YourDatabase;
GO


CREATE TRIGGER DDLTrigger_Sample
    ON DATABASE
    FOR CREATE_PROCEDURE, ALTER_PROCEDURE, DROP_PROCEDURE
AS
BEGIN
    SET NOCOUNT ON;
    DECLARE
        @EventData XML = EVENTDATA();
 
    DECLARE @ip varchar(48) = CONVERT(varchar(48), 
        CONNECTIONPROPERTY('client_net_address'));
 
    INSERT AuditDB.dbo.DDLEvents
    (
        EventType,
        EventDDL,
        EventXML,
        DatabaseName,
        SchemaName,
        ObjectName,
        HostName,
        IPAddress,
        ProgramName,
        LoginName
    )
    SELECT
        @EventData.value('(/EVENT_INSTANCE/EventType)[1]',   'NVARCHAR(100)'), 
        @EventData.value('(/EVENT_INSTANCE/TSQLCommand)[1]', 'NVARCHAR(MAX)'),
        @EventData,
        DB_NAME(),
        @EventData.value('(/EVENT_INSTANCE/SchemaName)[1]',  'NVARCHAR(255)'), 
        @EventData.value('(/EVENT_INSTANCE/ObjectName)[1]',  'NVARCHAR(255)'),
        HOST_NAME(),
        @ip,
        PROGRAM_NAME(),
        SUSER_SNAME();
END
GO

Note that when you create a DDL Trigger, just like a DML Trigger, it is enabled and will start working immediately. To disable it, you can run the following code:

USE YourDatabase;
GO


DISABLE TRIGGER [DDLTrigger_Sample] ON DATABASE;

And then to re-enable:

USE YourDatabase;
GO


ENABLE TRIGGER [DDLTrigger_Sample] ON DATABASE;

So now you can test the auditing capabilities by altering an existing procedure. Right-click a procedure in Object Explorer and choose Modify. Add the following line somewhere in the body:

-- testing audit

Then you can run a query against the DDLEvents table:

SELECT *
    FROM AuditDB.dbo.DDLEvents
    WHERE EventType = 'ALTER_PROCEDURE';

Assuming your system is relatively quiet, all you should see is the change above. Now to go one step further, you can examine the differences between the initial object and its most recent state using a query like this:

;WITH [Events] AS
(
    SELECT
        EventDate,
        DatabaseName,
        SchemaName,
        ObjectName,
        EventDDL,
        rnLatest = ROW_NUMBER() OVER 
        (
            PARTITION BY DatabaseName, SchemaName, ObjectName
            ORDER BY     EventDate DESC
        ),
        rnEarliest = ROW_NUMBER() OVER
        (
            PARTITION BY DatabaseName, SchemaName, ObjectName
            ORDER BY     EventDate
        )        
    FROM
        AuditDB.dbo.DDLEvents
)
SELECT
    Original.DatabaseName,
    Original.SchemaName,
    Original.ObjectName,
    OriginalCode = Original.EventDDL,
    NewestCode   = COALESCE(Newest.EventDDL, ''),
    LastModified = COALESCE(Newest.EventDate, Original.EventDate)
FROM
    [Events] AS Original
LEFT OUTER JOIN
    [Events] AS Newest
    ON  Original.DatabaseName = Newest.DatabaseName
    AND Original.SchemaName   = Newest.SchemaName
    AND Original.ObjectName   = Newest.ObjectName
    AND Newest.rnEarliest = Original.rnLatest
    AND Newest.rnLatest = Original.rnEarliest
    AND Newest.rnEarliest > 1
WHERE
    Original.rnEarliest = 1;

If you are tracking down a specific object or an object in a specific schema, you could put additional WHERE clauses against Original.ObjectName or Original.SchemaName. From here, you can take the values for "OriginalCode" and "NewestCode" and put them through your favorite diff tool to see what changes there have been. And you can also change the query slightly to retrieve the latest version of any procedure, and the version that preceded it - I'll leave that as an exercise for the reader.

What the above does not capture are other peripheral changes that can happen to a stored procedure. For example, what about moving a procedure to a different schema? You can change the DDL Trigger above in the following way to capture the ALTER_SCHEMA event:

USE YourDatabase;
GO


ALTER TRIGGER DDLTrigger_Sample
    ON DATABASE
    FOR CREATE_PROCEDURE, ALTER_PROCEDURE, DROP_PROCEDURE,
        ALTER_SCHEMA
AS
BEGIN
    -- ...

And how about rename? Unfortunately in SQL Server 2005, DDL Triggers were unable to observe calls to sp_rename (or manual renames through Management Studio). In SQL Server 2008 and above, however, a rename can be captured with the aptly-named RENAME event:

USE YourDatabase;
GO


ALTER TRIGGER DDLTrigger_Sample
    ON DATABASE
    FOR CREATE_PROCEDURE, ALTER_PROCEDURE, DROP_PROCEDURE,
        ALTER_SCHEMA, RENAME
AS
BEGIN
    -- ...

(In a future tip, I'll demonstrate how to restrict these additional auditing rows to specific objects or object types, so that you're not capturing all kinds of irrelevant information about changes to objects other than stored procedures.)

Some other considerations:

  • You may want to put in a cleanup routine that gets rid of "noise" more than <n> days old (but still keeping the set of objects that are important to you).
  • To validate that your auditing process is capturing all changes, you can check modify_date in sys.procedures. Of course this only works for procedures that haven't been dropped - only if they have been created, modified, renamed, or transfered to a different schema.
  • Security might be an issue, depending on what you want to accomplish. Allow me to elaborate:

    DDL Triggers will not be transparent to users - first of all, they can see them in the Object Explorer tree, so it won't be a big secret that they are there and operational. They also appear in execution plans; if users have this option enabled when they create or modify objects in Management Studio, they will see the query plan for statements such as INSERT AuditDB.dbo.DDLEvents.

    If you want to hide the definition of the DDL Trigger, you can encrypt it as follows:

    USE YourDatabase;
    GO
    
    
    ALTER TRIGGER DDLTrigger_Sample
        ON DATABASE
        WITH ENCRYPTION
        FOR -- ...
    

    This way, when users want to see what the trigger is doing, they will right-click to generate a script, but the following is what will happen:

    TITLE: Microsoft SQL Server Management Studio
    ------------------------------
    Script failed for DatabaseDdlTrigger 'DDLTrigger_Sample'.
    Property TextHeader is not available for DatabaseDdlTrigger
    '[DDLTrigger_Sample]'. This property may not exist for this
    object, or may not be retrievable due to insufficient access
    rights.  The text is encrypted.
    ...
    

    But users with sufficient privileges can still disable the trigger, as described above. And you can't even capture this event, much less prevent it (which DDL Triggers are sometimes used for). For more information, see these Connect items:

    So, assuming SQL Server 2008 or above, you could use an audit specification to capture DDL events as a backup (or instead). But, given all of this, if you have to go to these lengths to prevent people from circumventing your auditing capabilites, then maybe your problems are larger and not all that technical. I suspect that in most reasonable environments, you'll sleep fine at night simply locking down the audit table.

I hope this provides a decent starting point to protect your environment(s) with DDL Triggers. However, given the manual aspect of this approach as well as its limitations, it will likely be best to consider this a short-term plan, and look into more robust source control and recovery techniques in the longer term.

Next Steps



Last Updated: 2010-08-09 

相關推薦

SQL Server DDL Triggers to Track All Database Changes(SQLServer DDL 觸發器跟蹤所有資料庫變化)

Problem In a perfect world, only the DBA would have sa privileges, F5 would only ever be hit on purpose, every change would go throug

SQL Server hex string to varbinary conversion

在 MS SQL Server 裡做 hex string 與 binary 的轉換範例如下: From MSDN In SQL Server 2008, these conversions are even more easier since we added support directly in th

Use Logs to Track Redshift Database Cluster

Amazon Web Services is Hiring. Amazon Web Services (AWS) is a dynamic, growing business unit within Amazon.com. We are currently hiring So

使用SQL Server Management Studio操作replication時,要機器名登入,不要IP地址

如果你在使用SSMS(SQL Server Management Studio)登入SQL Server時,使用的是IP地址,如下圖所示: 當你操作replication時,會報錯:   從上面的錯誤提示可以看出,我們只能通過機器名(server name)來登入SSMS,才能夠操

Sql Server之旅——第十一站 簡單說說sqlserver的執行計劃

我們知道sql在底層的執行給我們上層人員開了一個視窗,那就是執行計劃,有了執行計劃之後,我們就清楚了那些爛sql是怎麼執行的,這樣 就可以方便的找到sql的缺陷和優化點。 一:執行計劃生成過程   說到執行計劃,首先要知道的是執行計劃大概生成的過程,這樣就可以做到就心中有數了

SQL server 存儲過程的建立和調

返回 ndt 簡單 系統變量 update object sele ring 句柄 存儲過程的建立和調用 --1.1準備測試需要的數據庫:test,數據表:物料表,采購表if not exists (select * from master.dbo.sysdatabas

Learning Note: SQL Server VS Oracle–Database architecture

ati sha sch segment undo log eat version course cau http://www.sqlpanda.com/2013/07/learning-note-sql-server-vs.html This is my learnin

【總算解決了】A network-related or instance-specific error occurred while establishing a connection to SQL Server

sql tle sqlserve 不知道 程序 tar ace product 自己的 給別人做的網站莫名其妙連接不上數據庫。百度了好多,總算知道自己的錯在哪了。 報 “A network-related or instance-specific error occ

SQL Server-2012 database query foundation

技術 height com sin idt log logical ati ica 1 Categories of query statements 2 Logical query processing 3 SQL Server-2012 database quer

SQL SERVER – Configuration Manager – Cannot Connect to WMI Provider. You Do Not Have Permission or The Server is Unreachable

hab ssi ima onf wmi manager ges ger connect 打開SQL SERVER Configuarion Manger 出現以下錯誤 SQL Server Configuration Manager—————————Cannot conn

A Sample To use Update, InnerJoin, When/Case, Exists in SQL Server

div enc sin date() end arc inner white from DECLARE @SINKKNO varchar(11),@KYOKAYMD varchar(8),@LASTUPDATEID varchar(5),@LASTUPDPGID varch

翻譯(九)——Clustered Indexes: Stairway to SQL Server Indexes Level 3

ext 找到 right 之間 基礎 任務 level ima 指示 原文鏈接:www.sqlservercentral.com/articles/Stairway+Series/72351/ Clustered Indexes: Stairway to SQL Serv

翻譯(十一) ——Reading Query Plans: Stairway to SQL Server Indexes Level 9

排序效率 left 成本 asp ima 選項卡 一起 create 支持 Reading Query Plans: Stairway to SQL Server Indexes Level 9 By David Durant, 2011/10/05 原文鏈接:h

SQL server 導出平面文件時出錯: The code page on Destination - 3_txt.Inputs[Flat File Destination Input].Columns[UserId] is 936 and is required to be 1252.

log 解決辦法 驗證 AI inpu image ans post BE 我在導出平面文件時:Error 0xc00470d4: Data Flow Task 1: The code page on Destination - 3_txt.Inputs[Flat File

Database SQL Server 2017

SQL 2017SQL Server 2017 : PreparationInstall SQL Server 2017 to build Database Server.Refer to the Microsoft Site about details of SQL Server 2017.? https:

SQL Server DDL觸發器

用戶名 eat exists lar value back ref tro get DDL 觸發器作用: DDL 觸發器主要用於防止對數據庫架構、視圖、表、存儲過程等進行的某些修改。 DDL 觸發器事件: DDL 觸發器在創建用來監視並響應該數據庫或服務器實例中的活動的

ArcSDE for SQL Server建庫提示:Case sensitive database is not supported

導語 今天有一個客戶諮詢,為什麼我的ArcGIS10.2 for SQL Server 建立企業級地理資料庫老是不成功,一直報Case sensitive database is not supported錯誤。 錯誤資訊 GP錯誤 Executing: Create

翻譯:Introduction to SQLSever Statistics SQL Server統計資訊簡介 Statistics and Execution Plans統計和執行計劃 Statistics Maintenance統計維護

《Pro SQL Server Internals, 2nd edition》的CHAPTER 3 Statistics中的Introduction to SQLSever Statistics SQL Server(P55~58)、Statistics and Execution Plans(P62~6

《Pro SQL Server Internals, 2nd edition》的CHAPTER 3 Statistics中的Introduction to SQL Server Statistics、Statistics and Execution Plans、Statistics Maintena

  《Pro SQL Server Internals》     作者: Dmitri Korotkevitch 出版社: Apress出版年: 2016-12-29頁數: 804定價: USD 59.99裝

《Pro SQL Server Internals, 2nd edition》(pdf已傳至群檔案)的CHAPTER 3 Statistics中的Introduction to SQL Server Statistics(P55~P58)、Statistics and Execution Plans

每次你改變聚簇索引鍵的值時,都會發生兩件事。 首先,SQL Server將行移動到聚簇索引頁鏈和資料檔案中的不同位置。 其次,它更新聚集索引鍵,行編號。 行編號被儲存起來而且要在所有非聚簇索引中更新。 對於I / O而言,這花銷可能很昂貴,尤其是在批處理更新的情況下。此外,它可以增加聚簇索引的碎片,並且在行編