【问题标题】:SQL Server Auto Incrementing Identity Per Customer(Tenant) With No GapsSQL Server 自动递增每个客户(租户)的身份,没有间隙
【发布时间】:2015-07-20 19:56:57
【问题描述】:

我们有一个多租户数据库,其中包含多个客户,每个客户都有一个这样的用户集合(省略从用户到客户的外键规范的简化示例):

CREATE TABLE dbo.Customers
(
   CustomerId INT NOT NULL IDENTITY(1, 1),
   Name NVARCHAR(256) NOT NULL
)


CREATE TABLE dbo.Users
(
   User INT NOT NULL IDENTITY(1, 1),
   CustomerId INT NOT NULL,
)

作为此设计的一部分,用户需要有一个会员编号,当我们设计这个时,我们决定使用 UserId 作为会员编号,但是随着所有事情的发展,这个要求已经增长,这不再是两个人的选择原因:

  1. 在每次服务器重新启动时升级到 2012 后,列会跳跃 1000 个值,我们使用了此处显示的解决方法:http://www.codeproject.com/Tips/668042/SQL-Server-2012-Auto-Identity-Column-Value-Jump-Is (-t272) 来阻止这种情况发生,但让我们意识到 IDENTITY( 1, 1) 还不够好。

  2. 我们现在真正想要的是确保每个客户的数字都会增加,但它必须是永久性的,并且一旦分配就不能更改。

显然,一个序列将不再起作用,因为它需要每个客户,我们还需要对每个客户/用户强制执行唯一约束,并确保该值一旦分配就永远不会改变,如果用户被删除也不会改变(虽然这不应该发生,因为我们不会删除用户而是将它们标记为已归档,但我想保证这不会影响它)。

以下是我编写的可以生成数字的示例,但使用此方法或类似方法的最佳方法是什么从不同的会话同时创建。

  ROW_NUMBER() OVER (ORDER BY i.UserId) + ISNULL((SELECT MAX(users.MembershipNumber)
                                                    FROM [User].Users users
                                                   WHERE users.Customers_CustomerId = i.Customers_CustomerId), 0)

编辑:澄清

很抱歉,我刚刚重新阅读了我的问题,但我没有说得足够清楚,我们不打算替换 UserId,我们对所有外键上使用的每个数据库标识符的差距和唯一性感到满意,我们正在寻找添加的是一个将显示给用户的 MembershipNumber,这就是为什么它需要按客户顺序排列,没有间隙,因为此会员号将用于提供给用户的卡上,因此需要是唯一的。

【问题讨论】:

  • 您为什么介意差距或每个客户有一个系列?不就是某种身份证吗?您的解决方案可能有效,但我不是 SELECT MAX() 的粉丝,因为它只有在您将数据库设置为一直锁定时才真正有效。理想情况下,您会使用快照隔离来减少争用,但会引入这样的问题。就我个人而言,我只是按顺序进行,并接受差距。
  • Stop making assumptions about IDENTITY,别再担心差距了——它们真的不是问题!
  • 感谢您的回复,我已经编辑了我的问题以澄清我的意思。
  • 阅读您的编辑后,我仍然认为我的回答完全符合您的需求。如果您不这么认为,请在评论中告诉我,以便我尝试改进它。

标签: sql-server uniqueidentifier


【解决方案1】:

既然您已经发现了身份列的问题以及如何解决它,我不会说它不够好。
但是,它似乎不适合您的需求,因为您希望用户编号增加每个客户

我建议将 User 列保留为 Identity 列和表的主键,并添加另一列以按客户指定 User 编号。此列也将是一个整数,其默认值为 UDF 的结果,它将计算每个客户的下一个数字 (see example in this post)。

您可以通过在 users 表上使用而不是更新触发器来保护该值不发生变化。

这种方式可以保留单列主键,任何您都有一个唯一的、连续的每个客户的用户编号。

更新
显然,it is impossible to send column values to a default constraint.
但是您仍然可以使用instead of insert 触发器来实现您的目标。

【讨论】:

  • 我已经尝试过了,但我似乎无法将 CustomerId 传递给默认约束函数,就像您可以通过检查约束传递列值一样。如果没有函数中行的客户 ID,它将无法工作。
  • 我们使用了一个代替插入触发器,虽然不理想,但看起来是唯一的出路。
  • 很高兴我能帮上忙!如果您找到更好的解决方案,我想了解一下。
【解决方案2】:

这是因为序列对象的默认缓存 sqlserver 实现。看到这个以前的线程 Identity increment is jumping in SQL Server database

如果差距是个问题,sql-server2012 已经引入了 Sequence 对象。这些您可以使用 NOCACHE 声明,因此重新启动服务器不会造成间隙。

【讨论】:

    【解决方案3】:

    我想分享我的想法。请看下文。

    1. 创建单独的表,其中包含如下所示的 CustomerID 和 Count 列。

      CREATE TABLE dbo.CustomerSequence
      (
         @CustomerID int,
         @Count  int
      );
      
    2. 编写某种存储过程,如下所示。

      CREATE PROC dbo.usp_GetNextValueByCustomerID
            @CustomerID int,
            @Count int OUTPUT
      AS
      BEGIN
          UPDATE dbo.CustomerSequence
          SET @Count = Count += Count
          WHERE CustomerID = @CustomerID;
      
      END
      
    3. 只需通过传递 CustomerID 调用上述存储过程并从中获取下一个序列值。

    【讨论】:

      【解决方案4】:

      如果您有多个用户同时添加新寄存器,我认为最好的办法是创建一个复合主键,其中用户是一个小字节(如果您的用户少于 255 个)并且增量数字是一个整数。然后,在添加新寄存器时,您会创建一个字符串主键,例如 'NN.xxxxxx' 。假设 [Number] 是您的增量编号并且 [Code] 是用户的代码(或本地机器分配的编号),您使用 DMax 函数分配新的 UserId,如下所示: NextNumber = Nz(DMax("Number", "clients", "Code=" & Me!code, 0) + 1

          UserId= code & "." & NextNumber
      

      在哪里 NN 是用户的代码 “。”用于分隔两个字段,并且 XXXX 是新号码

      【讨论】:

        猜你喜欢
        • 2018-04-05
        • 2018-07-10
        • 2011-06-01
        • 2016-08-13
        • 2019-02-14
        • 1970-01-01
        • 1970-01-01
        • 2021-02-09
        • 2021-04-08
        相关资源
        最近更新 更多