SQL Server DDL觸發器運用


一.本文所涉及的內容(Contents)

  1. 本文所涉及的內容(Contents)
  2. 背景(Contexts)
  3. 基礎知識(Rudimentary Knowledge)
  4. DDL運用場景(DDL Scene)
  5. 補充說明(Addon)
  6. 疑問(Questions)
  7. 參考文獻(References)

二.背景(Contexts)

  說到觸發器,大家都會想到這樣的使用場景:當一個表的數據修改了,運用DML觸發插入或者更新到其它表中;那DDL觸發器(SQL Server 2005引入的新功能)會運用到什么場景中呢?本文將為你講述4種運用DDL觸發器的場景:

  1) 禁止用戶修改和刪除表;

  2) 禁止用戶刪除數據庫;

  3) 記錄和監控某數據庫所有的DDL操作;

  4) 把DDL操作信息以郵件的形式主動發送通知和預警;

三.基礎知識(Rudimentary Knowledge)

  DDL觸發器是由修改數據庫對象的 DDL 語句(如以 CREATE、ALTER 或 DROP)激發。

  DDL觸發器支持BEFORE和AFTER事件觸發器,並在數據庫或模式級運行。通常,DDL觸發器用於監控數據庫中的重要事件。有時用它們來監控錯誤代碼。錯誤代碼可能會執行破壞數據庫或使數據庫不穩定的活動。更常見的情況是:在開發、測試和stage系統中用它們來了解和監控數據庫活動的動態。

  當監控GRANT和REVOKE權限語句時,它們也是有效的安全工具。

四.DDL運用場景(DDL Scene)

(一) 首先我們來看一個簡單的例子:創建數據庫DDL_DB和一個名為DatabaseLog的表,現在創建一個DDL觸發器:禁止用戶修改和刪除表,並進行提醒。執行下面的SQL腳本進行測試。

--Script1:
--創建測試數據庫
USE MASTER
GO
CREATE DATABASE DDL_DB

--創建DDL觸發器記錄表
USE DDL_DB
GO
CREATE TABLE [dbo].[DatabaseLog](
    [DatabaseLogID] [int] IDENTITY(1,1) NOT NULL,
    [PostTime] [datetime] NOT NULL,
    [ServerName] [sysname] NOT NULL,
    [LoginName] [sysname] NOT NULL,
    [DatabaseUser] [sysname] NOT NULL,
    [DatabaseName] [sysname] NOT NULL,
    [Schema] [sysname] NULL,
    [Object] [sysname] NULL,
    [TSQL] [nvarchar](max) NOT NULL,
    [Event] [sysname] NOT NULL,
    [XmlEvent] [xml] NOT NULL,
 CONSTRAINT [PK_DatabaseLog_DatabaseLogID] PRIMARY KEY NONCLUSTERED 
(
    [DatabaseLogID] ASC
) ON [PRIMARY]
) ON [PRIMARY]

--Script2:
--創建DDL觸發器:禁止修改或者刪除數據表
CREATE TRIGGER DDL_TableTrigger
ON DATABASE
FOR DROP_TABLE, ALTER_TABLE
AS
   PRINT '對不起,您不能對數據表進行操作,請聯系DBA'
   ROLLBACK ;

--測試刪除表
USE DDL_DB
GO
DROP TABLE [DatabaseLog]

wps_clip_image-14403_thumb

(Figure1:創建數據庫級別的DDL)

wps_clip_image-32149_thumb

(Figure2:返回的提示信息)

wps_clip_image-7628_thumb

(Figure3:SSMS返回的提示信息)

創建數據庫級別的DDL之后會出現在數據庫觸發器列表中,如Figure1;當執行刪除表的Drop等DDL命令的時候,就會出現Figure2的提示信息;如果是在SSMS中刪除表則會出現Figure3的提示信息。

 

(二) 在上面的基礎上再進行擴展,創建一個DDL觸發器:禁止用戶刪除數據庫,並進行提醒。

--Script3:
--禁止SQL Server服務器里刪除數據庫
CREATE TRIGGER DDL_DataBaseTrigger
ON ALL SERVER
FOR DROP_DATABASE
AS
    PRINT '對不起,您不能刪除數據庫,請聯系DBA' 
    ROLLBACK;

--測試刪除數據庫
USE MASTER
GO
DROP DATABASE [DDL_DB]

wps_clip_image-11348_thumb

(Figure4:創建服務器級別的DDL)

wps_clip_image-13353_thumb

(Figure5:返回的提示信息)

wps_clip_image-16272_thumb

(Figure6:SSMS返回的提示信息)

創建服務器級別的DDL之后會出現在服務器對象-觸發器的列表中,如Figure4;當執行刪除數據庫的Drop等DDL命令的時候,就會出現Figure5的提示信息;如果是在SSMS中刪除數據庫則會出現Figure6的提示信息。

 

(三) 很多時候在程序開發階段是不會禁用對數據庫的修改的,這些時候我們更希望是記錄數據庫的修改信息,方便對信息進行跟蹤檢查。使用 EVENTDATA 函數,可以捕獲有關激發 DDL 觸發器的事件的信息,此函數返回 xml 值。

前面已經創建了數據表DatabaseLog,創建下面的DDL_DatabaseLog觸發器,每當數據庫發生DDL事件,DDL觸發器就會把相關的DDL信息插入到DatabaseLog表,信息包括操作的時間,操作人,操作的SQL等。

執行Script5測試腳本,返回Figure7的信息,查詢DatabaseLog表,返回的記錄有2條,一條是創建表信息,一條是刪除表信息,如Figure8、Figure9所示。

--Script4:
--創建當前數據庫的DDL觸發器
USE DDL_DB
GO
-- =============================================
-- Author:        <聽風吹雨>
-- Create date:    <2013.05.03>
-- Description:    <記錄數據庫DDL操作>
-- Blog:        <http://www.cnblogs.com/gaizai/>
-- =============================================
CREATE TRIGGER [DDL_DatabaseLog]
ON DATABASE 
FOR DDL_DATABASE_LEVEL_EVENTS AS 
BEGIN
    SET NOCOUNT ON;
    DECLARE @data XML;
    DECLARE @schema sysname;
    DECLARE @object sysname;
    DECLARE @eventType sysname;
    SET @data = EVENTDATA();
    SET @eventType = @data.value('(/EVENT_INSTANCE/EventType)[1]', 'sysname');
    SET @schema = @data.value('(/EVENT_INSTANCE/SchemaName)[1]', 'sysname');
    SET @object = @data.value('(/EVENT_INSTANCE/ObjectName)[1]', 'sysname') 
    
    IF @object IS NOT NULL
        PRINT '  ' + @eventType + ' - ' + @schema + '.' + @object;
    ELSE
        PRINT '  ' + @eventType + ' - ' + @schema;
    IF @eventType IS NULL
        PRINT CONVERT(nvarchar(max), @data);
    INSERT [DDL_DB].[dbo].[DatabaseLog](
        [PostTime], 
        [ServerName], 
        [LoginName], 
        [DatabaseUser], 
        [DatabaseName],
        [Schema], 
        [Object], 
        [TSQL], 
        [Event], 
        [XmlEvent])
    VALUES(
        GETDATE(), 
        @data.value('(/EVENT_INSTANCE/ServerName)[1]', 'sysname'), 
        @data.value('(/EVENT_INSTANCE/LoginName)[1]', 'sysname'), 
        CONVERT(sysname, CURRENT_USER),
        @data.value('(/EVENT_INSTANCE/DatabaseName)[1]', 'sysname'),  
        CONVERT(sysname, @schema), 
        CONVERT(sysname, @object), 
        @data.value('(/EVENT_INSTANCE/TSQLCommand)[1]', 'nvarchar(max)'),
        @eventType,  
        @data
        );
END;

--Script5:測試DDL記錄
--禁用DDL 觸發器
DISABLE TRIGGER DDL_TableTrigger ON DATABASE;
GO
CREATE TABLE TestTable (a int)
GO
DROP TABLE TestTable;
GO

SELECT * FROM [DatabaseLog];
GO

wps_clip_image-15637_thumb

(Figure7:返回的提示信息)

wps_clip_image-3080_thumb

(Figure8:DatabaseLog表前半部分信息)

wps_clip_image-5273_thumb

(Figure9:DatabaseLog表后半部分信息)

 

(四) 我們可以使用DDL觸發器主動監控DDL語句的執行,當有對數據庫執行DDL就會觸發,我們把這些信息保存到表中,並且把操作用戶的HostName和修改的T-SQL以郵件的形式發送到指定的郵件。關於設置數據庫郵件可以參考:SQL Server 數據庫郵件。發送郵件的效果如Figure10。郵件部分參考:MS SQL監控數據庫的DDL操作

--Script5:
--創建當前數據庫的DDL觸發器
USE DDL_DB
GO
-- =============================================
-- Author:        <聽風吹雨>
-- Create date:    <2013.05.03>
-- Description:    <記錄數據庫DDL操作,發送郵件預警>
-- Blog:        <http://www.cnblogs.com/gaizai/>
-- =============================================
CREATE TRIGGER [DDL_DatabaseLog]
ON DATABASE 
FOR DDL_DATABASE_LEVEL_EVENTS AS 
BEGIN
    SET NOCOUNT ON;
    DECLARE @data XML;
    DECLARE @schema sysname;
    DECLARE @object sysname;
    DECLARE @eventType sysname;
    DECLARE @databaseName sysname;
    DECLARE @tableHTML  NVARCHAR(MAX);
    SET @data = EVENTDATA();
    SET @eventType = @data.value('(/EVENT_INSTANCE/EventType)[1]', 'sysname');
    SET @schema = @data.value('(/EVENT_INSTANCE/SchemaName)[1]', 'sysname');
    SET @object = @data.value('(/EVENT_INSTANCE/ObjectName)[1]', 'sysname');
    SET @databaseName = @data.value('(/EVENT_INSTANCE/DatabaseName)[1]', 'sysname');
    
    IF @object IS NOT NULL
        PRINT '  ' + @eventType + ' - ' + @schema + '.' + @object;
    ELSE
        PRINT '  ' + @eventType + ' - ' + @schema;
    IF @eventType IS NULL
        PRINT CONVERT(nvarchar(max), @data);
    INSERT [DDL_DB].[dbo].[DatabaseLog](
        [PostTime], 
        [ServerName], 
        [LoginName], 
        [DatabaseUser], 
        [DatabaseName],
        [Schema], 
        [Object], 
        [TSQL], 
        [Event], 
        [XmlEvent])
    VALUES(
        GETDATE(), 
        @data.value('(/EVENT_INSTANCE/ServerName)[1]', 'sysname'), 
        @data.value('(/EVENT_INSTANCE/LoginName)[1]', 'sysname'), 
        CONVERT(sysname, CURRENT_USER),
        @databaseName,  
        CONVERT(sysname, @schema), 
        CONVERT(sysname, @object), 
        @data.value('(/EVENT_INSTANCE/TSQLCommand)[1]', 'nvarchar(max)'),
        @eventType,  
        @data
        );

    SET @tableHTML =
        N'<H1>DDL Event</H1>' +
        N'<table border="0">' +
    N'<tr><th>PostTime</th><th>ServerName</th><th>LoginName</th><th>DatabaseUser</th><th>DatabaseName</th><th>Object</th>' +
        N'<th>TSQL</th></tr>' +
        CAST((SELECT
        td = [PostTime],'',
        td = [ServerName],'',
        td = [LoginName],'',
        td = [DatabaseUser],'',
        td = [DatabaseName],'',
        td = [Object],'',
        td = TSQL,''
        FROM [DDL_DB].[dbo].[DatabaseLog]
        WHERE DatabaseLogID =(SELECT MAX(DatabaseLogID) FROM [DDL_DB].[dbo].[DatabaseLog])
        FOR XML PATH('tr'), TYPE) AS NVARCHAR(MAX)) +
        N'</table>';
        
    DECLARE @subjectStr  NVARCHAR(MAX);
    SET @subjectStr = 'DDL Event - DataBaseName: ' + @databaseName;
        EXEC msdb.dbo.sp_send_dbmail
        @profile_name = 'DataBase_DDL_Event',
        @recipients='bbspediy@126.com',
        @subject = @subjectStr,
        @body = @tableHTML,
        @body_format = 'HTML';
END;

wps_clip_image-30426_thumb

(Figure10:郵件收到的預警)

五.補充說明(Addon)

(一) 關於DML、DDL、DCL、TCL的解釋:

DML

DML is abbreviation of Data Manipulation Language. It is used to retrieve, store, modify, delete, insert and update data in database.

Examples: SELECT, UPDATE, INSERT statements

DDL

DDL is abbreviation of Data Definition Language. It is used to create and modify the structure of database objects in database.

Examples: CREATE, ALTER, DROP statements

DCL

DCL is abbreviation of Data Control Language. It is used to create roles, permissions, and referential integrity as well it is used to control access to database by securing it.

Examples: GRANT, REVOKE statements

TCL

TCL is abbreviation of Transactional Control Language. It is used to manage different transactions occurring within a database.

Examples: COMMIT, ROLLBACK statements

(二) 關於DML與DDL運用場景的一些區別:

DML 觸發器可以看作是一種特殊的存儲過程,可以保證系統保持其完整性,在系統中進行級聯更新或強行業務規則。通過INSERTED 和 DELETED ,我們可以檢索哪些列被更新了。DML觸發器的本質就是當這兩個發生數據修改時自動運行的存儲過程。

DDL 觸發器的構建主要是為了安全,或者根據部門的需求對系統所進行的變更進行通報。通過使用 EVENTDATA( ) 函數,可以在觸發器中使用XML信息。

(三) 如果是線上的系統,可以考慮做下面的限制:在工作時間,不允許修改任何存儲過程,否則回滾,示例代碼如下:IF DATEPART(hour, GETDATE()) >=9 AND DATEPART(hour, GETDATE()) <= 17

(四) 一些維護DDL的SQL腳本:

--啟用DDL 觸發器
ENABLE TRIGGER DDL_TableTrigger ON DATABASE;
--禁用DDL 觸發器
DISABLE TRIGGER ddlDatabaseTriggerLog ON DATABASE;
--刪除DDL 觸發器
DROP TRIGGER ddlDatabaseTriggerLog ON DATABASE;

--禁用當前數據庫中所有數據庫級別的DDL 觸發器
DISABLE TRIGGER ALL ON DATABASE
--禁用服務器實例中所有服務器級別的DDL 觸發器
DISABLE TRIGGER ALL ON ALL SERVER

(五) 所有的DDL事件可以查看DDL 事件,也可以通過下面的SQL進行查看:

--獲取有關DDL 觸發器可觸發的事件或事件組的信息
SELECT * FROM sys.trigger_event_types
--查看觸發器的依賴關系
SELECT * FROM sys.sql_expression_dependencies
SELECT * FROM sys.dm_sql_referenced_entities
SELECT * FROM sys.dm_sql_referencing_entities
--獲取有關數據庫范圍內的觸發器的信息
SELECT * FROM sys.triggers
--獲取有關激發觸發器的數據庫事件的信息
SELECT * FROM sys.trigger_events
SELECT * FROM sys.trigger_events AS a 
LEFT join sys.triggers AS b
ON a.object_id=b.object_id
WHERE name = 'ddlDatabaseTriggerLog'
--獲取有關服務器范圍內的觸發器的信息
SELECT * FROM sys.server_triggers
SELECT * FROM sys.server_trigger_events
--查看數據庫范圍內的觸發器的定義
SELECT * FROM sys.sql_modules

(六) 在執行Script3的時候如果你正在使用SSMS打開這個數據庫(SPID)的話,那有可能不是出現Figure5的錯誤信息,而是出現Figure11的錯誤,這是因為你沒有關閉SPID這些窗口,我還沒有在程序連接的情況測試是否會返回這些信息:

wps_clip_image-1961_thumb

(Figure11:Figure5可能出現的)

(七) 如果你想修改DDL觸發器的內容,那么你不能直接Alter DDL,而應該是先執行Drop DDL,之后在Create DDL。

(八) 之前已經創建了DDL_TableTrigger和DDL_DatabaseLog觸發器,這兩個觸發器都是在DDL_DB數據庫中創建的,當我們需要修改DDL觸發器,應該觸發對象從小到大進行修改,即DDL_TableTrigger(表)到DDL_DatabaseLog(數據庫)進行修改。

如Figure12所示,如果只修改DDL_TableTrigger(Drop、Create),再執行下面的腳本將會出現Figure13的錯誤(還沒找到官方理論描述)。解決辦法就是對DDL_DatabaseLog進行創建創建(Drop、Create)。

--測試刪除表
USE MASTER
GO
DROP DATABASE [DDL_DB]

wps_clip_image-27698_thumb

(Figure12:DDL觸發器列表)

wps_clip_image-170_thumb

(Figure13:錯誤信息)

七.疑問(Questions)

(一) 刪除DDL觸發器是否也可以觸發一個事件呢?不然如何防止用戶先刪除DDL觸發器之后再做DDL操作呢?難道是用戶權限?

解答:第一種方法,可以對DDL觸發器進行權限控制;第二種方式就是在服務器級別加一個DROP的觸發器,可以監控各個數據庫的DDL觸發器;下圖Figure14是DDL_DatabaseLog被刪除時的預警;

wps_clip_image-10302_thumb

(Figure14:刪除DDL觸發器)

(二) 能對所有數據庫進行DDL監控?一條DDL預警能實現?

解答:可以在DDL_DatabaseLog把 ON DATABASE 設置為ON All SERVER,這樣就可以監控整個服務器實例,下圖Figure15是Logon_DB的DDL預警;

wps_clip_image-16633_thumb

(Figure15:刪除DDL觸發器)

八.參考文獻(References)

MS SQL Server:DDL 觸發器

DDL 觸發器(msdn)

DDL 觸發器(technet)

DML 觸發器(technet)

登錄觸發器(technet)

SQL Server 2005 - Default Trace (默認跟蹤)

MS SQL監控數據庫的DDL操作(郵件通知)

DDL觸發器分為

引用 DDL觸發器介紹(Oracle)

DDL觸發器

SQL SERVER – What is – DML, DDL, DCL and TCL – Introduction and Examples

管理觸發器安全性

使用 EVENTDATA 函數

CREATE TRIGGER (Transact-SQL)

sp_send_dbmail (Transact-SQL)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM