【问题标题】:Ensure Uniqueness with Triggers使用触发器确保唯一性
【发布时间】:2012-08-28 14:55:31
【问题描述】:

所以,我尝试使用以下触发器,以便在更新表中的行时,它将特定列设置为 8 个随机字母数字字符的字符串。触发器工作正常,只是结果必须是唯一的。在触发器之前它将是空白的,因此不会在服务器上强制执行此唯一性。这个想法是,一旦客户在流程中走得足够远,他们就会得到这个字符串,但之前不会,因为我们有很多没有走得那么远的客户。

触发器:

CREATE TRIGGER test_rand
ON test_trigger
AFTER UPDATE 
AS
    UPDATE test_trigger set col2=(select
    Random_String =
    substring(x,(abs(checksum(newid()))%36)+1,1)+
    substring(x,(abs(checksum(newid()))%36)+1,1)+
    substring(x,(abs(checksum(newid()))%36)+1,1)+
    substring(x,(abs(checksum(newid()))%36)+1,1)+
    substring(x,(abs(checksum(newid()))%36)+1,1)+
    substring(x,(abs(checksum(newid()))%36)+1,1)+
    substring(x,(abs(checksum(newid()))%36)+1,1)+
    substring(x,(abs(checksum(newid()))%36)+1,1)

from
          (select x='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ') a) WHERE col2=''; 
GO

这个触发器是我使用一个只有两列col1col2 的简单表运行的测试。我为一堆行更新col1col2 获得看似随机的值,但我知道两行可能获得相同的数字,如果可能的话我想防止这种情况发生。

我曾想过在不存在该字符串的地方检查 select 语句本身,但我无法想出一个好方法来做到这一点。

我是触发器的新手,所以如果我遗漏了一些简单的东西或者应该搜索一下,那么我为浪费您的时间而道歉。据我所知,这甚至不可能,但我想我应该在放弃并尝试其他方法之前先问清楚。

此外,在执行更新的脚本中没有这样做的原因是,我无法从客户端得到关于哪些脚本实际执行此操作的直接答案。如果这不可能,我将只编写一个由 Windows 任务计划程序运行的任务,但我希望这样做。

我的解决方案

根据 usr 告诉我的内容,我创建了以下代码来满足我的需求:

CREATE TRIGGER test_rand
ON test_trigger
AFTER UPDATE 
AS
    DECLARE @Ins_Col3 int;
    DECLARE Ins_Curs CURSOR FOR SELECT col3 FROM inserted;
    DECLARE @Ref char(8);

    OPEN Ins_Curs

    FETCH NEXT FROM Ins_Curs
    INTO @Ins_Col3;

    WHILE @@FETCH_STATUS = 0
    BEGIN;
        SET @Ref = LEFT(NEWID(), 8);

        WHILE(EXISTS(SELECT col2 from test_trigger WHERE col2=@Ref))
        BEGIN;
            SET @Ref = LEFT(NEWID(), 8);
        END;

        UPDATE test_trigger SET test_trigger.col2=@Ref FROM inserted WHERE test_trigger.col3=@Ins_Col3;
            FETCH NEXT FROM Ins_Curs
        INTO @Ins_Col3;
    END;

我必须添加 col3 作为自动递增的 id 值,以便我可以识别每一行。 因此,col1 仅存在于此,以便我可以通过更新语句进行更改。

【问题讨论】:

  • 您的代码应该可以工作,但我不确定您可以从 GUID 的前 8 个字节中获得多好的随机数。见blogs.msdn.com/b/oldnewthing/archive/2008/06/27/8659071.aspx
  • 我认为您应该尝试说服您的客户使用 UniqueIdentifier 字段。 NEWID() 函数将始终为您提供唯一的字符串。您将获得性能提升,因为您不会试图确保您的价值不在数据库中。 UniqueIdentifier 只有 128 位。您的八个字符字段至少为 64 位。额外的 64 位性能提升是值得的。
  • 这与性能无关。这是关于让客户使用它作为识别推荐他们的人的一种方式,并且客户认为不再拥有它可能对人们输入或写下它是一个问题。这也适用于测试系统,甚至可能不会持续超过一两个月。如果他们确实决定保留更长时间,那么我可能会与他们讨论延长它的时间。

标签: sql-server sql-server-2008 triggers


【解决方案1】:

您应该使用 GUID 而不是混合 8 个字符的字符串。在 SQL Server 中,它是 UniqueIdentifier 列类型。这是一篇关于它的文章:http://msdn.microsoft.com/en-us/library/ms190215(v=sql.105).aspx

【讨论】:

  • 我希望我可以使用它,但我认为我不能。如果我正确阅读了链接,为了确保 UniqueIdentifier 是唯一的,我需要将该约束添加到行中。但是,我需要允许该列为 NULL,直到发生违反约束的操作。我可能会与客户交谈,看看他们是否可以更改它,以便我可以使用它。感谢您指出这一点,因为我的谷歌搜索没有出现。
  • 保证唯一标识符值始终是唯一值。您是正确的,列中的唯一约束不允许两行为空。 (在这一点上,Oracle 比 SQL Server 更好)。也许您总是可以在 UniqueIdentifier 列中有一个值,并使用另一列来表示您想要的空值?或者也许相信 UniqueIdentifier 是唯一的? (会的)。
  • 我刚刚重新阅读了我链接的文章。他们指定 UniqueIdentifier 值 cab 出现多次的部分,除非您放置约束需要更多说明。他们的意思是,如果您的应用程序代码获得一个 GUID 值,您可以将该单个 GUID 值插入到任意数量的行中。这篇文章并不是说您在插入 GUID 值后可能会意外获得重复的 GUID 值。 NEWID 函数或获取新 GUID 的 appcode 返回的值将始终是唯一的。
  • 好吧,这比我昨天读的更有意义。那部分似乎不太清楚,我不打算浪费时间创建一个巨大的桌子来测试它。感谢您的时间和帮助,不幸的是,客户不想要那么久的东西,所以虽然我仍在使用 NEWID() 函数,但我只取了一小部分,所以我仍然需要检查唯一性并使用了 usr 下面建议的稍长版本。
【解决方案2】:

如果您想防止这种情况发生,请在您的触发器中包含一个WHILE 循环,只要生成的字符串因违反唯一性条件而无效,就会循环:

WHILE 0=0
BEGIN
 SET_TO_NEW_VALUE
 IF(NEW_VALUE_IS_UNIQUE)
  BREAK
END

如果值不是唯一的,您也可以ROLLBACKRAISEERROR。取决于你想发生什么。

【讨论】:

  • 问题是一次可能不止一行会触发这个问题,所以如果我按照你的建议去做,除非有办法遍历所有受影响的行,否则它们都会得到相同的值通过我一直找不到的更新。
  • 您可以使用 INSERTED 和 DELETED 伪表迭代所有受影响的行。您可以使用游标对它们进行迭代,也可以使用更新语句对其进行批量分配。
  • 这就是我所做的。我将在问题中发布我完成的代码,以防其他人想看到它。
  • 此解决方案可行,但性能可能较差。对我来说,使用 GUID(唯一标识符)多 64 位的成本对于性能和更简单的代码来说是值得的。此答案的未来观众应该仔细权衡他们的选择。
猜你喜欢
  • 2011-03-10
  • 1970-01-01
  • 2020-12-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-20
相关资源
最近更新 更多