单独输出EVENTDATA的特定部分以用于DDL触发器?

时间:2015-06-30 13:01:38

标签: sql sql-server-2008 triggers ddl 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)

它输出以下内容:

  

执行了以下SQL语句:

     

ALTER TABLE dbo.TABLE

     

ADD TestColumn varchar(50)

     

下一步是弄清楚如何挑出添加的列名

     

Msg 3609,Level 16,State 2,Line 2

     

交易在触发器中结束。批次已中止。

最终目标是挑出“TestColumn”以确保它不包含可能破坏内部程序的特定字符。 “TestColumn”将是一个有效的名称,但“Test.Column”不会,例如。

将单个输出并将其传递给新变量(“@ColumnName”?)以检查不需要的字符的最佳方法是什么?

1 个答案:

答案 0 :(得分:1)

You need to bear in mind that an alter statement can add more than one column. If you look at the XML generated for an alter column statement you will get a better idea:

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>

So since you can add multiple columns, you need to check multiple columns, to get your columns lists you can use:

SELECT  ColumnName = Cols.value('.', 'SYSNAME')
FROM    EVENTDATA().nodes('EVENT_INSTANCE/AlterTableActionList/Create/Columns') x (Cols);

Then you could simply use EXISTS to check against a list of reserved names etc, and check for unwanted characters:

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

Another thing to consider is that sp_rename will fire a different event, and you probably want to track this too. The XML here looks like:

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

So here you would need to track it slightly differently, since it can only be one column at a time, you don't need to worry about nodes:

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';