如下針對某個數據庫在建立數據表時調用觸發器,並將建立該數據表的用戶帳戶寫入到Windows的Event Log中。sql
CREATE TRIGGER reminder ON DATABASE FOR CREATE_TABLE AS DECLARE @str NVARCHAR(100) SET @str=suser_sname() + N'create a new table' RAISERROR(@str,10,1) WITH LOG
建立完DDL觸發器後,因該觸發器所在的等級,而會顯示在「Object Explorer」中不一樣的位置,上述是建立數據庫等級的觸發器,所以,顯示在某個數據庫的「Programmability」中「Database Triggers」節點之下。數據庫
建立完DDL觸發器後,請嘗試使用下列語法建立數據表,因爲該DDL觸發器使用RAISERROR系統函數搭配WITH LOG選項,這會將信息寫入到Windows操做系統的事件日誌中。接着,能夠在事件查看器中查詢到事件內容。ide
因爲觸發器默認都是與引起該觸發器的語法包在相同事務內一塊兒執行,所以,咱們能夠經過ROLLBACK命令回滾先前指令對系統的影響,讓用戶的DROP_TABLE、ALTER_TABLE DDL語法沒法在DB內執行。函數
CREATE TRIGGER safety ON DATABASE FOR DROP_TABLE, ALTER_TABLE AS PRINT N'Before drop or alter table,you should drop trigger safety!!!!' ROLLBACK
完成限制的DDL觸發器以後,咱們經過下列語法測試該觸發器:測試
ALTER TABLE tblAbc ADD c2 INT
因爲直接被觸發器回滾(Rollback),SQL Server將返回錯誤信息。操作系統
另外,經過DDL觸發器搭配EVENTDATA系統函數,經由SQL Server 2005所提供的XQuery語句的Query函數查詢,取出想要的數據。再將全部DDL行爲記錄到另一個數據表中。3d
USE [AdventureWorks2012] GO /****** Object: Table [dbo].[DatabaseLog] Script Date: 2014/12/31 11:33:19 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE TABLE [dbo].[DatabaseLog]( [DatabaseLogID] [int] IDENTITY(1,1) NOT NULL, [PostTime] [datetime] NOT NULL, [DatabaseUser] [sysname] NOT NULL, [Event] [sysname] NOT NULL, [Schema] [sysname] NULL, [Object] [sysname] NULL, [TSQL] [nvarchar](max) NOT NULL, [XmlEvent] [xml] NOT NULL, CONSTRAINT [PK_DatabaseLog_DatabaseLogID] PRIMARY KEY NONCLUSTERED ( [DatabaseLogID] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Primary key for DatabaseLog records.' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DatabaseLog', @level2type=N'COLUMN',@level2name=N'DatabaseLogID' GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'The date and time the DDL change occurred.' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DatabaseLog', @level2type=N'COLUMN',@level2name=N'PostTime' GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'The user who implemented the DDL change.' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DatabaseLog', @level2type=N'COLUMN',@level2name=N'DatabaseUser' GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'The type of DDL statement that was executed.' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DatabaseLog', @level2type=N'COLUMN',@level2name=N'Event' GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'The schema to which the changed object belongs.' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DatabaseLog', @level2type=N'COLUMN',@level2name=N'Schema' GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'The object that was changed by the DDL statment.' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DatabaseLog', @level2type=N'COLUMN',@level2name=N'Object' GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'The exact Transact-SQL statement that was executed.' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DatabaseLog', @level2type=N'COLUMN',@level2name=N'TSQL' GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'The raw XML data generated by database trigger.' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DatabaseLog', @level2type=N'COLUMN',@level2name=N'XmlEvent' GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Audit table tracking all DDL changes made to the AdventureWorks database. Data is captured by the database trigger ddlDatabaseTriggerLog.' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DatabaseLog' GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Primary key (nonclustered) constraint' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DatabaseLog', @level2type=N'CONSTRAINT',@level2name=N'PK_DatabaseLog_DatabaseLogID' GO
CREATE TRIGGER [ddlDatabaseTriggerLog] 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 [dbo].[DatabaseLog] ( [PostTime], [DatabaseUser], [Event], [Schema], [Object], [TSQL], [XmlEvent] ) VALUES ( GETDATE(), CONVERT(sysname, CURRENT_USER), @eventType, CONVERT(sysname, @schema), CONVERT(sysname, @object), @data.value('(/EVENT_INSTANCE/TSQLCommand)[1]', 'nvarchar(max)'), @data ); END; GO
CREATE TABLE TestTable(a int); ALTER TABLE TestTable ADD b nvarchar(10); DROP TABLE TestTable; GO
SELECT * FROM dbo.DatabaseLog ORDER BY DatabaseLogID;
若要刪除存放觸發器所產生log的數據表dbo.DatabaseLog時,必須先刪除使用到這個數據表的觸發器。不然會有奇怪的錯誤信息以下:日誌
固然,你在刪除任何對象前,能夠先檢查一下對象依賴關係,以確認是否能夠刪除。xml
建立具備主外鍵關機的表,來測試利用DDL觸發器避免數據表被刪除。對象
USE [AdventureWorks2012] GO /****** Object: Table [dbo].[Test] Script Date: 2014/12/31 15:10:19 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE TABLE [dbo].[Test]( [id] [int] IDENTITY(1,1) NOT NULL, [DetailID] [int] NULL, CONSTRAINT [PK_Test] PRIMARY KEY CLUSTERED ( [id] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO /****** Object: Table [dbo].[TestDetail] Script Date: 2014/12/31 15:10:19 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO SET ANSI_PADDING ON GO CREATE TABLE [dbo].[TestDetail]( [ID] [int] IDENTITY(1,1) NOT NULL, [Desc] [varchar](50) NULL, CONSTRAINT [PK_TestDetail] PRIMARY KEY CLUSTERED ( [ID] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO SET ANSI_PADDING ON GO ALTER TABLE [dbo].[Test] WITH CHECK ADD CONSTRAINT [FK_Test_DetailID] FOREIGN KEY([DetailID]) REFERENCES [dbo].[TestDetail] ([ID]) GO ALTER TABLE [dbo].[Test] CHECK CONSTRAINT [FK_Test_DetailID] GO
搭配EventData系統函數與XQuery語句解析其內容,例如,以VALUE函數判斷對象名稱,然後決定是否容許用戶刪除。將safety觸發器修改了下,若是用戶刪除的是dbo.Test數據表,纔會出現錯誤信息。
ALTER TRIGGER safety ON DATABASE FOR DROP_TABLE AS DECLARE @data XML=EVENTDATA() DECLARE @SchemaName nvarchar(max) DECLARE @TableName nvarchar(max) SET @SchemaName=EVENTDATA().value('(/EVENT_INSTANCE/SchemaName)[1]','sysname') SET @TableName=EVENTDATA().value('(/EVENT_INSTANCE/ObjectName)[1]','sysname') IF @SchemaName='dbo' AND @TableName='Test' BEGIN DECLARE @msg NVARCHAR(MAX)=N'You can''t Delete the table: [' + @SchemaName + '].[' + @TableName + ']' RAISERROR(@msg,16,1) ROLLBACK TRAN END
正常狀況而言,數據表會被FOREIGN KEY條件約束保護,而沒法刪除,可是FOREIGN KEY約束只保護主數據表,這裏對應dbo.TestDetail,而不保護參照數據表,這裏對應dbo.Test。
利用XQuery中的Value,取得SchemaName以及ObjectName,再經過假設句判斷是否爲所要保護的對象,便可保護目標數據表。此時,若執行以下的語句:
DROP TABLE dbo.Test
將會獲得以下的錯誤信息:
若要刪除數據庫級別的DDL觸發器,只要參照以下語法便可:
DROP TRIGGER safety ON DATABASE DROP TABLE dbo.Test
在刪除DDL觸發器時,要搭配ON DATABASE或ON ALL SERVER選項,不然SQL Server會覺得要刪除的是通常DML觸發器,所以,會返回找不到對象的錯誤信息。
查看[ddlDatabaseTriggerLog]觸發器記錄的信息:
SELECT * FROM dbo.DatabaseLog ORDER BY DatabaseLogID
最後,顯示EVENTDATA函數返回的XML內容以下。
<EVENT_INSTANCE> <EventType>DROP_TABLE</EventType> <PostTime>2014-12-31T15:30:01.010</PostTime> <SPID>53</SPID> <ServerName>WIN-LLPKR5BUV6S</ServerName> <LoginName>WIN-LLPKR5BUV6S\Administrator</LoginName> <UserName>dbo</UserName> <DatabaseName>AdventureWorks2012</DatabaseName> <SchemaName>dbo</SchemaName> <ObjectName>Test</ObjectName> <ObjectType>TABLE</ObjectType> <TSQLCommand> <SetOptions ANSI_NULLS="ON" ANSI_NULL_DEFAULT="ON" ANSI_PADDING="ON" QUOTED_IDENTIFIER="ON" ENCRYPTED="FALSE" /> <CommandText>DROP TABLE dbo.Test</CommandText> </TSQLCommand> </EVENT_INSTANCE>
另外,SQL Server也針對觸發器提供查詢元數據的方式,之前述建立的DDL觸發器爲對象,查詢語法以下:
SELECT * FROM sys.triggers WHERE name=’safety’ SELECT definition FROM sys.sql_modules WHERE object_id=(SELECT object_id FROM sys.triggers WHERE name=’safety’)