SQL 服务器自动递增每个客户(租户)的身份,没有间隙

SQL Server Auto Incrementing Identity Per Customer(Tenant) With No Gaps

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

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. 我们现在真正想要的是确保每个客户的数量递增,但它必须是永久的,一旦分配就不能改变。

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

下面是我写的可以生成数字的示例,但是使用这个或类似的东西的最佳方法是什么,以确保每个 customer/user 的唯一顺序值不会出现任何问题因为可以从不同会话同时创建用户。

  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,这就是为什么它需要按客户顺序排列且没有间隙,因为该会员编号将用于提供给用户的卡,因此需要是唯一的。

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

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

您可以通过对用户使用 instead of update 触发器来保护该值不被更改 table。

通过这种方式保留单列主键,每个客户都有一个唯一的、连续的用户编号。

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

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

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

我想分享一下我的想法。请看下面。

  1. 创建单独的 table,它将包含如下所示的 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 调用上面的存储过程,并从中获取下一个 Sequence 值。

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

    UserId= code & "." & NextNumber

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