SQL Server 不会跟踪对表的特定更改。如果您想要或需要这种级别的详细信息,您需要创建一个 DDL 触发器(在 SQL Server 2005 中引入),该触发器可以捕获某些特定事件甚至事件类别,并将这些更改记录到您创建的历史表中。
DDL 触发器是“后”触发器;没有“代替”选项。但是,如果您想禁止某项操作,只需发出 ROLLBACK 即可取消所发生的事情。
DDL Triggers 的 MSDN 页面有很多关于如何捕获特定事件(即ALTER TABLE)和使用返回 XML 的 EVENTDATA 函数来获取触发事件的详细信息的信息触发器,包括已执行的确切 SQL 查询。事实上,Use the EVENTDATA Function 的 MSDN 页面甚至有创建 DDL 触发器以捕获 ALTER TABLE 语句的简单示例(在“
ALTER TABLE 和 ALTER DATABASE 事件”部分)并创建 DDL 触发器以将事件捕获到日志表(在“示例”部分中)。由于所有 ALTER TABLE 命令都会触发此触发器,因此您需要解析出哪些是特定于您要查找的内容。而且,也许现在您知道这是一个选项,需要捕获的不仅仅是添加列(即删除列、更改数据类型和/或 NULLability 等)。
需要注意的是,您可以为ALTER_TABLE等数据库范围的事件创建一个DLL触发器ON ALL SERVER。
如果您想查看任何事件或事件类的 XML 结构,请转到:
http://schemas.microsoft.com/sqlserver/2006/11/eventdata/
并单击“当前版本:”链接。如果您想查看特定事件或事件类,只需在触发器的“FOR”子句(包括下划线)中使用的事件名称上进行搜索(通常是浏览器中的 Control-F)。以下是ALTER_TABLE 事件的架构:
<xs:complexType name="EVENT_INSTANCE_ALTER_TABLE">
<xs:sequence>
<!-- Basic Envelope -->
<xs:element name="EventType" type="SSWNAMEType"/>
<xs:element name="PostTime" type="xs:string"/>
<xs:element name="SPID" type="xs:int"/>
<!-- Server Scoped DDL -->
<xs:element name="ServerName" type="PathType"/>
<xs:element name="LoginName" type="SSWNAMEType"/>
<!-- DB Scoped DDL -->
<xs:element name="UserName" type="SSWNAMEType"/>
<!-- Main Body -->
<xs:element name="DatabaseName" type="SSWNAMEType"/>
<xs:element name="SchemaName" type="SSWNAMEType"/>
<xs:element name="ObjectName" type="SSWNAMEType"/>
<xs:element name="ObjectType" type="SSWNAMEType"/>
<xs:element name="Parameters" type="EventTag_Parameters" minOccurs="0"/>
<xs:element name="AlterTableActionList" type="AlterTableActionListType" minOccurs="0"/>
<xs:element name="TSQLCommand" type="EventTag_TSQLCommand"/>
</xs:sequence>
</xs:complexType>
这是一个非常简单的测试,看看它是如何工作的以及生成的 EventData XML 的样子:
IF (EXISTS(
SELECT *
FROM sys.server_triggers sst
WHERE sst.name = N'CaptureAlterTable'
))
BEGIN
DROP TRIGGER CaptureAlterTable ON ALL SERVER;
END;
GO
CREATE TRIGGER CaptureAlterTable
ON ALL SERVER -- capture events for all databases
FOR ALTER_TABLE -- only capture ALTER TABLE events
AS
PRINT CONVERT(NVARCHAR(MAX), EVENTDATA()); -- Display in "Messages" tab in SSMS
GO
首先我们在 tempdb 中创建一个简单的真实表(临时表不会捕获这些事件):
USE [tempdb];
CREATE TABLE dbo.MyAlterTest (Col2 INT NULL);
接下来我们添加一列。我们从不同的数据库执行此操作,以确保 XML 捕获对象存在的数据库而不是当前数据库。请注意单词 alTeR Table tempDB.dbo.MyALTERTest ... DATEcreated 的大小写,以便与 XML 中的内容进行比较。
USE [master];
alTeR Table tempDB.dbo.MyALTERTest ADD DATEcreated DATETIME NOT NULL;
您应该在“消息”选项卡(我添加的 cmets)中看到以下内容:
<EVENT_INSTANCE>
<EventType>ALTER_TABLE</EventType>
<PostTime>2014-12-15T10:53:04.523</PostTime>
<SPID>55</SPID>
<ServerName>_{server_name}_</ServerName>
<LoginName>_{login_name}_</LoginName>
<UserName>dbo</UserName>
<DatabaseName>tempdb</DatabaseName> <!-- casing is based on database definition -->
<SchemaName>dbo</SchemaName>
<ObjectName>MyAlterTest</ObjectName> <!-- casing is based on object definition -->
<ObjectType>TABLE</ObjectType>
<AlterTableActionList>
<Create>
<Columns>
<Name>DATEcreated</Name> <!-- casing is taken from executed query -->
</Columns>
</Create>
</AlterTableActionList>
<TSQLCommand>
<SetOptions ANSI_NULLS="ON" ANSI_NULL_DEFAULT="ON" ANSI_PADDING="ON" QUOTED_IDENTIFIER="ON" ENCRYPTED="FALSE"/>
<CommandText>alTeR Table tempDB.dbo.MyALTERTest ADD DATEcreated DATETIME NOT NULL;
</CommandText>
</TSQLCommand>
</EVENT_INSTANCE>
如果捕获每列的详细信息(即 NULL / NOT NULL、数据类型等)而不仅仅是名称,那就太好了,但如果需要,可以从 CommandText 元素中解析出这些细节。