【问题标题】:SQL Server trigger not firing when there is a character case change字符大小写更改时 SQL Server 触发器未触发
【发布时间】:2014-04-16 21:55:25
【问题描述】:

我正在使用脚本在更新、插入和删除表时对其进行审核。我有一个审计表,其中放置了各种信息,如果它是更新的话,包括 OLD 和 NEW 更改。

但是,如果有人进入记录并将例如“帮助”更改为“帮助”,我的触发器不会触发。我似乎无法在脚本中找到可以更改此行为的位置。

这是我的触发代码:

ALTER TRIGGER [dbo].[tr_addresses] 
ON [dbo].[addresses] FOR INSERT, UPDATE, DELETE
AS
    DECLARE @bit INT ,
        @field INT ,
        @maxfield INT ,
        @char INT ,
        @fieldname VARCHAR(128) ,
        @TableName VARCHAR(128) ,
        @PKCols VARCHAR(1000) ,
        @sql VARCHAR(2000), 
        @UpdateDate VARCHAR(21) ,
        @UserName VARCHAR(128) ,
        @Type CHAR(1) ,
        @PKSelect VARCHAR(1000)

--You will need to change @TableName to match the table to be audited
SELECT @TableName = 'addresses'

-- date and user
SELECT @UserName = SYSTEM_USER ,
        @UpdateDate = CONVERT(VARCHAR(8), GETDATE(), 112) 
                + ' ' + CONVERT(VARCHAR(12), GETDATE(), 114)

-- Action
IF EXISTS (SELECT * FROM inserted)
        IF EXISTS (SELECT * FROM deleted)
                SELECT @Type = 'U'
        ELSE
                SELECT @Type = 'I'
        ELSE
                SELECT @Type = 'D'

-- 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 = @TableName
        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 = @TableName
        AND     CONSTRAINT_TYPE = 'PRIMARY KEY'
        AND     c.TABLE_NAME = pk.TABLE_NAME
        AND     c.CONSTRAINT_NAME = pk.CONSTRAINT_NAME

IF @PKCols IS NULL
BEGIN
        RAISERROR('no PK on table %s', 16, -1, @TableName)
        RETURN
END

SELECT         @field = 0, 
        @maxfield = MAX(ORDINAL_POSITION) 
        FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = @TableName
WHILE @field < @maxfield
BEGIN
        SELECT @field = MIN(ORDINAL_POSITION) 
                FROM INFORMATION_SCHEMA.COLUMNS 
                WHERE TABLE_NAME = @TableName 
                AND ORDINAL_POSITION > @field
        SELECT @bit = (@field - 1 )% 8 + 1
        SELECT @bit = POWER(2,@bit - 1)
        SELECT @char = ((@field - 1) / 8) + 1
        IF SUBSTRING(COLUMNS_UPDATED(),@char, 1) & @bit > 0 OR @Type IN ('I','D')
        BEGIN
                SELECT @fieldname = COLUMN_NAME 
                        FROM INFORMATION_SCHEMA.COLUMNS 
                        WHERE TABLE_NAME = @TableName 
                        AND ORDINAL_POSITION = @field
                SELECT @sql = '
insert Audit (  Type, 
                TableName, 
                PK, 
                FieldName, 
                OldValue, 
                NewValue, 
                UpdateDate, 
                UserName)
select ''' + @Type + ''',''' 
        + @TableName + ''',' + @PKSelect
        + ',''' + @fieldname + ''''
        + ',convert(varchar(1000),d.' + @fieldname + ')'
        + ',convert(varchar(1000),i.' + @fieldname + ')'
        + ',''' + @UpdateDate + ''''
        + ',''' + @UserName + ''''
        + ' 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

我做错了什么?

谢谢你, 特拉维斯

【问题讨论】:

  • 生成的 SQL 内部的比较:where i.' + @fieldname + ' &lt;&gt; d.' + @fieldname 取决于为列(数据库)定义的排序规则。 SQL Server 中的默认设置是使用不区分大小写的比较。您需要更改数据库(或表、列)的排序规则。但这可能会改变您的应用程序的行为。

标签: sql-server tsql triggers


【解决方案1】:

您可以使用COLLATE clause 来强制特定比较使用区分大小写的排序规则。大多数情况下,默认排序规则不区分大小写,因此这两个词被认为是相同的。比如:

SELECT @sql = '
insert Audit (  Type, 
            TableName, 
            PK, 
            FieldName, 
            OldValue, 
            NewValue, 
            UpdateDate, 
            UserName)
select ''' + @Type + ''',''' 
    + @TableName + ''',' + @PKSelect
    + ',''' + @fieldname + ''''
    + ',convert(varchar(1000),d.' + @fieldname + ')'
    + ',convert(varchar(1000),i.' + @fieldname + ')'
    + ',''' + @UpdateDate + ''''
    + ',''' + @UserName + ''''
    + ' from #ins i full outer join #del d'
    + @PKCols
    + ' where i.' + @fieldname + ' <> d.' + @fieldname
    --New line here - applies the specific collation to
    --the previous comparison operation
    + ' collate Latin1_General_CS_AS '
    + ' or (i.' + @fieldname + ' is null and  d.' + @fieldname + ' is not null)' 
    + ' or (i.' + @fieldname + ' is not null and  d.' + @fieldname + ' is null)' 

Latin1_General_CS_AS 是否是您使用的正确排序规则由您决定 - 通常,找到用于这些列的任何排序规则,然后将 CI 部分替换为 CS,以获得区分大小写的等效项。

【讨论】:

    猜你喜欢
    • 2018-02-15
    • 2015-01-20
    • 2021-09-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-24
    相关资源
    最近更新 更多