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觸發器的場景:html

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

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

  3) 記錄和監控某數據庫全部的DDL操做;express

  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)spa

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條,一條是建立表信息,一條是刪除表信息,如Figure八、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)

相關文章
相關標籤/搜索