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脚本进行测试。

SQL Server DDL触发器运用
--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]
SQL Server DDL触发器运用

SQL Server DDL触发器运用

(Figure1:创建数据库级别的DDL)

SQL Server DDL触发器运用

(Figure2:返回的提示信息)

SQL Server DDL触发器运用

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

创建数据库级别的DDL之后会出现在数据库触发器列表中,如Figure1;当执行删除表的Drop等DDL命令的时候,就会出现Figure2的提示信息;如果是在SSMS中删除表则会出现Figure3的提示信息。

 

(二) 在上面的基础上再进行扩展,创建一个DDL触发器:禁止用户删除数据库,并进行提醒。

SQL Server 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]
SQL Server DDL触发器运用

SQL Server DDL触发器运用

(Figure4:创建服务器级别的DDL)

SQL Server DDL触发器运用

(Figure5:返回的提示信息)

SQL Server DDL触发器运用

(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所示。

SQL Server DDL触发器运用
--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
SQL Server DDL触发器运用

SQL Server DDL触发器运用

(Figure7:返回的提示信息)

SQL Server DDL触发器运用

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

SQL Server DDL触发器运用

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

 

(四) 我们可以使用DDL触发器主动监控DDL语句的执行,当有对数据库执行DDL就会触发,我们把这些信息保存到表中,并且把操作用户的HostName和修改的T-SQL以邮件的形式发送到指定的邮件。关于设置数据库邮件可以参考:SQL Server 数据库邮件。发送邮件的效果如Figure10。邮件部分参考:MS SQL监控数据库的DDL操作

SQL Server 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;
SQL Server DDL触发器运用

SQL Server DDL触发器运用

(Figure10:邮件收到的预警)

SQL Server DDL触发器运用,布布扣,bubuko.com

SQL Server DDL触发器运用

上一篇:成为android工程师的31+个小技巧(1),h5开发移动端


下一篇:Linux学习日志