挑出 EVENTDATA 的特定部分用于 DDL 触发器?
Single out specific parts of EVENTDATA for use in a DDL Trigger?
当前触发器如下:
CREATE TRIGGER TestTrigger
ON DATABASE
FOR ALTER_TABLE
AS
BEGIN
SET NOCOUNT ON;
DECLARE @FULL_STATEMENT SYSNAME
SELECT @FULL_STATEMENT = EVENTDATA().value('(/EVENT_INSTANCE/TSQLCommand/CommandText)[1]','nvarchar(max)')
print('The following SQL statement was executed:')
print('')
print (@FULL_STATEMENT)
print('')
print('Next step is to work out how to single out the added column name')
ROLLBACK
END
然后我运行下面SQL声明:
USE [a_Database]
ALTER TABLE dbo.TABLE
ADD TestColumn varchar(50)
它输出以下内容:
The following SQL statement was executed:
ALTER TABLE dbo.TABLE
ADD TestColumn varchar(50)
Next step is to work out how to single out the added column name
Msg 3609, Level 16, State 2, Line 2
The transaction ended in the trigger. The batch has been aborted.
最终目标是挑出"TestColumn" 以确保它不包含可能破坏内部程序的特定字符。 "TestColumn" 将是一个有效名称,但 "Test.Column" 则不是,例如。
将它挑出来并将其传递给新变量(“@ColumnName”?)以检查不需要的字符的最佳方法是什么?
您需要记住,一条 alter 语句可以添加多个列。如果您查看为 alter column 语句生成的 XML,您会得到一个更好的主意:
ALTER TABLE dbo.EventTest ADD NewColumn1 INT, NewColumn2 INT;
----------------------------
<EVENT_INSTANCE>
<EventType>ALTER_TABLE</EventType>
<PostTime>2015-06-30T14:28:30.790</PostTime>
<SPID>67</SPID>
<ServerName>XXXXXX</ServerName>
<LoginName>XXXXXX</LoginName>
<UserName>dbo</UserName>
<DatabaseName>XXXXXX</DatabaseName>
<SchemaName>dbo</SchemaName>
<ObjectName>EventTest</ObjectName>
<ObjectType>TABLE</ObjectType>
<AlterTableActionList>
<Create>
<Columns>
<Name>NewColumn1</Name>
<Name>NewColumn2</Name>
</Columns>
</Create>
</AlterTableActionList>
<TSQLCommand>
<SetOptions ANSI_NULLS="ON" ANSI_NULL_DEFAULT="ON" ANSI_PADDING="ON" QUOTED_IDENTIFIER="ON" ENCRYPTED="FALSE" />
<CommandText>ALTER TABLE dbo.EventTest ADD NewColumn1 INT, NewColumn1 INT;</CommandText>
</TSQLCommand>
</EVENT_INSTANCE>
因此,由于您可以添加多列,因此您需要检查多列,以获取您可以使用的列列表:
SELECT ColumnName = Cols.value('.', 'SYSNAME')
FROM EVENTDATA().nodes('EVENT_INSTANCE/AlterTableActionList/Create/Columns') x (Cols);
然后您可以简单地使用 EXISTS
来检查保留名称列表等,并检查不需要的字符:
IF EXISTS
( SELECT 1
FROM ( SELECT ColumnName = Cols.value('.', 'SYSNAME')
FROM EVENTDATA().nodes('EVENT_INSTANCE/AlterTableActionList/Create/Columns') x (Cols)
) AS t
WHERE PATINDEX('%[.@!-]%', t.ColumnName) > 0 -- USE PATTERN MATCH TO CHECK FOR UNWANTED CHARACTERS
OR EXISTS
( SELECT 1
FROM dbo.ReservedColumnNames AS rc
WHERE rc.Name = t.ColumnName
)
)
BEGIN
RAISERROR(...)
ROLLBACK;
END
另一件需要考虑的事情是 sp_rename
会触发一个不同的事件,您可能也想跟踪它。这里的 XML 看起来像:
<EVENT_INSTANCE>
<EventType>RENAME</EventType>
....
<SchemaName>dbo</SchemaName>
<ObjectName>NewColumn</ObjectName>
<ObjectType>COLUMN</ObjectType>
<TargetObjectName>EventTest</TargetObjectName>
<TargetObjectType>TABLE</TargetObjectType>
<NewObjectName>NewColumn2</NewObjectName>
<Parameters>
<Param>dbo.EventTest.NewColumn</Param>
<Param>NewColumn2</Param>
<Param>COLUMN</Param>
</Parameters>
<TSQLCommand>
<SetOptions ANSI_NULLS="ON" ANSI_NULL_DEFAULT="ON" ANSI_PADDING="ON" QUOTED_IDENTIFIER="ON" ENCRYPTED="FALSE" />
<CommandText>EXECUTE SP_RENAME 'dbo.EventTest.NewColumn', 'NewColumn2', 'COLUMN';</CommandText>
</TSQLCommand>
</EVENT_INSTANCE>
所以在这里你需要稍微不同地跟踪它,因为它一次只能是一列,你不需要担心节点:
SELECT EVENTDATA().value('EVENT_INSTANCE[1]/NewObjectName[1]', 'SYSNAME')
WHERE EVENTDATA().value('EVENT_INSTANCE[1]/EventType[1]', 'VARCHAR(13)') = 'RENAME'
AND EVENTDATA().value('EVENT_INSTANCE[1]/ObjectType[1]', 'VARCHAR(13)') = 'COLUMN';
当前触发器如下:
CREATE TRIGGER TestTrigger
ON DATABASE
FOR ALTER_TABLE
AS
BEGIN
SET NOCOUNT ON;
DECLARE @FULL_STATEMENT SYSNAME
SELECT @FULL_STATEMENT = EVENTDATA().value('(/EVENT_INSTANCE/TSQLCommand/CommandText)[1]','nvarchar(max)')
print('The following SQL statement was executed:')
print('')
print (@FULL_STATEMENT)
print('')
print('Next step is to work out how to single out the added column name')
ROLLBACK
END
然后我运行下面SQL声明:
USE [a_Database]
ALTER TABLE dbo.TABLE
ADD TestColumn varchar(50)
它输出以下内容:
The following SQL statement was executed:
ALTER TABLE dbo.TABLE
ADD TestColumn varchar(50)
Next step is to work out how to single out the added column name
Msg 3609, Level 16, State 2, Line 2
The transaction ended in the trigger. The batch has been aborted.
最终目标是挑出"TestColumn" 以确保它不包含可能破坏内部程序的特定字符。 "TestColumn" 将是一个有效名称,但 "Test.Column" 则不是,例如。
将它挑出来并将其传递给新变量(“@ColumnName”?)以检查不需要的字符的最佳方法是什么?
您需要记住,一条 alter 语句可以添加多个列。如果您查看为 alter column 语句生成的 XML,您会得到一个更好的主意:
ALTER TABLE dbo.EventTest ADD NewColumn1 INT, NewColumn2 INT;
----------------------------
<EVENT_INSTANCE>
<EventType>ALTER_TABLE</EventType>
<PostTime>2015-06-30T14:28:30.790</PostTime>
<SPID>67</SPID>
<ServerName>XXXXXX</ServerName>
<LoginName>XXXXXX</LoginName>
<UserName>dbo</UserName>
<DatabaseName>XXXXXX</DatabaseName>
<SchemaName>dbo</SchemaName>
<ObjectName>EventTest</ObjectName>
<ObjectType>TABLE</ObjectType>
<AlterTableActionList>
<Create>
<Columns>
<Name>NewColumn1</Name>
<Name>NewColumn2</Name>
</Columns>
</Create>
</AlterTableActionList>
<TSQLCommand>
<SetOptions ANSI_NULLS="ON" ANSI_NULL_DEFAULT="ON" ANSI_PADDING="ON" QUOTED_IDENTIFIER="ON" ENCRYPTED="FALSE" />
<CommandText>ALTER TABLE dbo.EventTest ADD NewColumn1 INT, NewColumn1 INT;</CommandText>
</TSQLCommand>
</EVENT_INSTANCE>
因此,由于您可以添加多列,因此您需要检查多列,以获取您可以使用的列列表:
SELECT ColumnName = Cols.value('.', 'SYSNAME')
FROM EVENTDATA().nodes('EVENT_INSTANCE/AlterTableActionList/Create/Columns') x (Cols);
然后您可以简单地使用 EXISTS
来检查保留名称列表等,并检查不需要的字符:
IF EXISTS
( SELECT 1
FROM ( SELECT ColumnName = Cols.value('.', 'SYSNAME')
FROM EVENTDATA().nodes('EVENT_INSTANCE/AlterTableActionList/Create/Columns') x (Cols)
) AS t
WHERE PATINDEX('%[.@!-]%', t.ColumnName) > 0 -- USE PATTERN MATCH TO CHECK FOR UNWANTED CHARACTERS
OR EXISTS
( SELECT 1
FROM dbo.ReservedColumnNames AS rc
WHERE rc.Name = t.ColumnName
)
)
BEGIN
RAISERROR(...)
ROLLBACK;
END
另一件需要考虑的事情是 sp_rename
会触发一个不同的事件,您可能也想跟踪它。这里的 XML 看起来像:
<EVENT_INSTANCE>
<EventType>RENAME</EventType>
....
<SchemaName>dbo</SchemaName>
<ObjectName>NewColumn</ObjectName>
<ObjectType>COLUMN</ObjectType>
<TargetObjectName>EventTest</TargetObjectName>
<TargetObjectType>TABLE</TargetObjectType>
<NewObjectName>NewColumn2</NewObjectName>
<Parameters>
<Param>dbo.EventTest.NewColumn</Param>
<Param>NewColumn2</Param>
<Param>COLUMN</Param>
</Parameters>
<TSQLCommand>
<SetOptions ANSI_NULLS="ON" ANSI_NULL_DEFAULT="ON" ANSI_PADDING="ON" QUOTED_IDENTIFIER="ON" ENCRYPTED="FALSE" />
<CommandText>EXECUTE SP_RENAME 'dbo.EventTest.NewColumn', 'NewColumn2', 'COLUMN';</CommandText>
</TSQLCommand>
</EVENT_INSTANCE>
所以在这里你需要稍微不同地跟踪它,因为它一次只能是一列,你不需要担心节点:
SELECT EVENTDATA().value('EVENT_INSTANCE[1]/NewObjectName[1]', 'SYSNAME')
WHERE EVENTDATA().value('EVENT_INSTANCE[1]/EventType[1]', 'VARCHAR(13)') = 'RENAME'
AND EVENTDATA().value('EVENT_INSTANCE[1]/ObjectType[1]', 'VARCHAR(13)') = 'COLUMN';