【问题标题】:SQL Server Localized Case-Sensitivity Comparison in Trigger/If-Statement触发器/If 语句中的 SQL Server 本地化大小写敏感性比较
【发布时间】:2017-03-15 02:23:24
【问题描述】:

我为审计目的创建了一些触发器,它们将旧(现有)字段值与UPDATEINSERT 上的新值进行比较,如果存在更改,它将更改的值插入到[AuditEntry] 表。没有什么神奇或开创性的。触发器运行良好,但有一个例外:当用户仅更改值的 case 时,触发器未检测到更改。 (例如:旧值“san francisco”更改为新值“San Francisco”未检测到。)

注意到这个错误后,我尝试将触发器更改为类似于以下内容以检查是否区分大小写(没有运气):

ALTER TRIGGER [dbo].[gtr_MyTable_Audit] 
   ON  [dbo].[MyTable] 
   FOR INSERT, UPDATE
AS 
BEGIN
    SET NOCOUNT ON;

    DECLARE @OldValue NVARCHAR(2000),
        @NewValue NVARCHAR(2000);

    ...

    SELECT @NewValue = [LocalizedName] COLLATE SQL_Latin1_General_CP1_CS_AS FROM INSERTED;  // Added COLLATE SQL_Latin1_General_CP1_CS_AS
    SELECT @OldValue = [LocalizedName] COLLATE SQL_Latin1_General_CP1_CS_AS FROM DELETED;   // Added COLLATE SQL_Latin1_General_CP1_CS_AS

    IF (@NewValue != @OldValue)
        BEGIN
            INSERT INTO [dbo].[AuditEntry] (OldValue, NewValue, ...) VALUES (@OldValue, @NewValue, ...);
        END

    ...
END

添加COLLATE SQL_Latin1_General_CP1_CS_AS 并没有解决问题,所以我显然不明白如何进行这种比较。

我的问题是:

如何正确比较 IF 语句中的两个值以确保区分大小写? (最好不要在数据库/服务器级别更改排序规则?)

注意:您可能已经从字段名称中猜到了,[MyTable].[LocalizedName] 中包含的值可能来自任何语言(英语、日语、俄语等),所以我很怀疑无论如何,尝试使用SQL_Latin1_General_CP1_CS_AS 可能是“不可以”。

(我在 SO 上看到 some examples 使用 CASTVARBINARY 进行比较,但我不确定这是否是一个合理的方法。)

【问题讨论】:

  • 您的触发器有一个重大缺陷!它假设在插入或删除中只会有一行。这不是 sql server 的操作方式。每次操作都会触发一次触发器。您需要将此设置为基于集合的逻辑。此外,您没有指定 varchar 的比例,因此它将使用默认大小。在这种情况下,它恰好是 30,但您应该始终指定大小以避免出现问题。
  • 为简洁起见,已从示例中删除了基于集合的逻辑(用... 表示),如果您觉得它使示例更清晰,我可以将其重新添加到示例中。
  • 不需要。我理解这个问题,但这个例子很可怕。 :) 我会使用二进制排序规则,因为您可能有任何语言的值。
  • 看看这个问题。 stackoverflow.com/questions/35583412/… srutzky 有一个很棒的答案。
  • 你试过IF (@NewValue != @OldValue COLLATE SQL_Latin1_General_CP1_CS_AS).. 并且只是选择了值

标签: sql-server triggers localization sql-server-2014 case-sensitive


【解决方案1】:

请试试这个。在 if 语句中有 Collat​​e 以进行比较。

   DECLARE @Old NVARCHAR(100)
   DECLARE @New NVARCHAR(100)

   SELECT @Old = Name from Deleted
   SELECT @New = Name from Inserted

   IF(@New COLLATE Latin1_General_CS_AS  != @Old COLLATE Latin1_General_CS_AS)
         PRINT 'Not Equal'
   ELSE
         PRINT 'Equal'

【讨论】:

    【解决方案2】:

    您可以尝试使用字符串的 ASCII 值,这样 ALTER TRIGGER [dbo].[gtr_MyTable_Audit] 开启 [dbo].[MyTable] 插入,更新 作为 开始 设置无计数;

    DECLARE @OldValue NVARCHAR(2000),
        @NewValue NVARCHAR(2000);
    
    ...
    
    SELECT @NewValue = [LocalizedName] COLLATE SQL_Latin1_General_CP1_CS_AS FROM INSERTED;  // Added COLLATE SQL_Latin1_General_CP1_CS_AS
    SELECT @OldValue = [LocalizedName] COLLATE SQL_Latin1_General_CP1_CS_AS FROM DELETED;   // Added COLLATE SQL_Latin1_General_CP1_CS_AS
    
    IF (ASCII(@NewValue) != ASCII(@OldValue))
        BEGIN
            INSERT INTO [dbo].[AuditEntry] (OldValue, NewValue, ...) VALUES (@OldValue, @NewValue, ...);
        END
    
    ...
    

    结束

    由于“s”的值不同于“S”且“f”的值不同于“F”,因此两个字符串将具有不同的 ASCII 值,并且会在字母大小写更改时插入您想要的行。

    【讨论】:

      猜你喜欢
      • 2014-06-09
      • 1970-01-01
      • 2015-12-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-26
      • 1970-01-01
      相关资源
      最近更新 更多