“SQL Server中的审计触发器”附近的语法不正确,“”

时间:2015-08-07 15:20:53

标签: sql sql-server sql-server-2012

我已经使用SQL来查询信息一段时间了,但我是创建触发器的新手。我和同事已经意识到我们的本地数据没有审计跟踪,因此我正在努力学习纠正这一点。不要担心,这只是背景 - 我不是在这里要求指南(虽然任何链接都会有所帮助......令人惊讶的是我读过的文章很少,实际上解释了他们的任何代码意味着什么)。这是在SQL Server 2012上。

所以,我认为我想要开始测试它的代码,但问题是每次触发器触发时我都会在第7行得到Incorrect syntax near ','。作为参考,这是触发器:

   -- =============================================
-- Author:      L. LeBlanc
-- Create date: 8/6/2015
-- Description: Testing auditing functionality
-- =============================================
ALTER TRIGGER [dbo].[testAuditTrigger]
   ON [SalesForce].[dbo].[DistinctTest]
   FOR INSERT, UPDATE, DELETE
AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    declare @id uniqueidentifier,
    @changedate varchar(21),
    @op nvarchar(50),
    @cols nvarchar(max),
    @rc bigint,
    @user varchar(128),
    @act int,
    @bitcols int,
    @bit int,
    @field int,
    @char INT,
    @maxfield int,
    @fieldname varchar(128),
    @sql varchar(2000),
    @PKCols VARCHAR(1000),
    @PKSelect VARCHAR(1000),
    @auditcols int

    select @user = system_user,
    @changedate = convert(varchar(8), getdate(), 112) + ' ' + convert(varchar(12), getdate(), 114)  

    -- determine action
    if exists (select * from inserted)
    begin
        if exists (select * from deleted)
            set @act = '0' -- update
        else
            set @act = '1' -- insert
    end
    else
        set @act = '2' -- delete

        -- get list of columns
    SELECT * INTO #ins FROM inserted
    SELECT * INTO #del FROM deleted

    -- Get primary key columns for full outer join
    SELECT @PKCols = COALESCE(@PKCols + ' and', ' on') 
               + ' i.' + c.COLUMN_NAME + ' = d.' + c.COLUMN_NAME
       FROM   INFORMATION_SCHEMA.TABLE_CONSTRAINTS pk ,
              INFORMATION_SCHEMA.KEY_COLUMN_USAGE c
       WHERE   pk.TABLE_NAME = 'DistinctTest'
       AND     CONSTRAINT_TYPE = 'PRIMARY KEY'
       AND     c.TABLE_NAME = pk.TABLE_NAME
       AND     c.CONSTRAINT_NAME = pk.CONSTRAINT_NAME

    -- Get primary key select for insert
    SELECT @PKSelect = COALESCE(@PKSelect+'+','') 
       + '''<' + COLUMN_NAME 
       + '=''+convert(varchar(100),
        coalesce(i.' + COLUMN_NAME +',d.' + COLUMN_NAME + '))+''>''' 
       FROM    INFORMATION_SCHEMA.TABLE_CONSTRAINTS pk ,
               INFORMATION_SCHEMA.KEY_COLUMN_USAGE c
       WHERE   pk.TABLE_NAME = 'DistinctTest'
       AND     CONSTRAINT_TYPE = 'PRIMARY KEY'
       AND     c.TABLE_NAME = pk.TABLE_NAME
       AND     c.CONSTRAINT_NAME = pk.CONSTRAINT_NAME


    if @act = '0'
    begin -- convert ye olde binarye to text
        set @bitcols = COLUMNS_UPDATED()
        SELECT TABLE_NAME, COLUMN_NAME,
            COLUMNPROPERTY(OBJECT_ID(TABLE_SCHEMA + '.' + TABLE_NAME),
            COLUMN_NAME, 'ColumnID') AS COLUMN_ID
        FROM SalesForce.INFORMATION_SCHEMA.COLUMNS
        WHERE TABLE_NAME = 'DistinctTest';
    end
    else
        set @cols = ''

    select @auditcols = count(*)
    from SalesForce.INFORMATION_SCHEMA.COLUMNS
        WHERE TABLE_NAME = 'DistinctTest_Audit'

    SELECT @field = 0, 
       @maxfield = MAX(ORDINAL_POSITION) 
       FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'DistinctTest'
    WHILE @field < @maxfield
    BEGIN
       SELECT @field = MIN(ORDINAL_POSITION) 
               FROM INFORMATION_SCHEMA.COLUMNS 
               WHERE TABLE_NAME = 'DistinctTest' 
               AND ORDINAL_POSITION > @field
       SELECT @bit = (@field - 1 )% @auditcols + 1 
       SELECT @bit = POWER(2,@bit - 1)
       SELECT @char = ((@field - 1) / @auditcols) + 1
       IF SUBSTRING(COLUMNS_UPDATED(),@char, 1) & @bit > 0
                                       OR @act IN ('1','2') -- insert or delete
       BEGIN
               SELECT @fieldname = COLUMN_NAME 
                       FROM INFORMATION_SCHEMA.COLUMNS 
                       WHERE TABLE_NAME = 'DistinctTest' 
                       AND ORDINAL_POSITION = @field
               SELECT @sql = CONCAT('
            insert DistinctTest.Audit (    Id, 
               Operation, 
               ColumnsModified, 
               ChangeDate, 
               ChangeUser)
            select ''', @id , ''',''' 
                , @op , ''',' , @cols
                , ',''' , @changedate , ''''
                , ',''' , @user , ''''
                , ' from #ins i full outer join #del d'
                , @PKCols
                , ' where i.' , @fieldname , ' <> d.' , @fieldname 
                , ' or (i.' , @fieldname , ' is null and  d.'
                                , @fieldname
                                , ' is not null)' 
                                , ' or (i.' + @fieldname + ' is not null and  d.' 
                                , @fieldname
                                , ' is null)' )
               EXEC (@sql)
       END
END

END

当我在触发器上使用sp_helptext时,它会给出与触发器开始时相同的结果。我看了几个例子,我正在努力弄清楚在这种特殊情况下究竟是什么让我的触发器有所不同。据我所知,根据编译器,第7行是ON [SalesForce].[dbo].[DistinctTest]

我也承认我对SQL编译器的工作方式知之甚少,所以我不知道“第7行”是否有可能实际上是用词不当而且错误是在身体中有一些逻辑我的代码但是,根据我的阅读,sp_helptext生成了编译器实际读取的脚本的准确打印输出。我希望不必经历这个特定错误的整个触发器,因为它看起来很简单。

简单也是让我难过的原因 - 我在第7行附近没有看到额外的逗号,这个触发器的声明看起来与我在别处看到的大多数相同。我之前也做了一些测试触发器,但它们只针对一种DML操作而不是三种。

1 个答案:

答案 0 :(得分:1)

引发错误是因为您的触发器具有动态sql,并且在包含在动态sql之前至少没有设置其中一个变量。这将导致无效的sql语句。如果要查看动态sql,可以在执行之前添加print或select语句来输出@SQL的内容。您必须使用SSMS来查看它,但它有助于调试。