另一列的上下文中的SQL Server唯一自动增量列

时间:2010-02-05 04:26:32

标签: sql-server sql-server-2005 auto-increment

假设该表有两列:

ParentEntityId int foreign key
Number int

ParentEntityId是另一个表的外键。

Number本地标识,即它在单ParentEntityId内是唯一的。

通过这两列上的唯一键可以轻松实现唯一性。

如何使Number在插入ParentEntityId的上下文中自动递增?


附录1

为澄清问题,这是一个摘要。

ParentEntity有多个ChildEntity,每个ChiildEntity在其Number的上下文中应具有唯一的增量ParentEntity


附录2

ParentEntity视为客户

ChildEntity视为订单

因此,每个客户的订单应编号为1,2,3等等。

3 个答案:

答案 0 :(得分:9)

嗯,这种类型的列没有原生支持,但您可以使用触发器实现它:

CREATE TRIGGER tr_MyTable_Number
ON MyTable
INSTEAD OF INSERT
AS

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE

BEGIN TRAN;

WITH MaxNumbers_CTE AS
(
    SELECT ParentEntityID, MAX(Number) AS Number
    FROM MyTable
    WHERE ParentEntityID IN (SELECT ParentEntityID FROM inserted)
)
INSERT MyTable (ParentEntityID, Number)
    SELECT
        i.ParentEntityID,
        ROW_NUMBER() OVER
        (
            PARTITION BY i.ParentEntityID
            ORDER BY (SELECT 1)
        ) + ISNULL(m.Number, 0) AS Number
    FROM inserted i
    LEFT JOIN MaxNumbers_CTE m
        ON m.ParentEntityID = i.ParentEntityID

COMMIT

未经测试,但我确信它会起作用。如果你有一个主键,你也可以将它实现为AFTER触发器(我不喜欢使用INSTEAD OF触发器,当你需要在6个月后修改它们时,它们更难理解。)

只是为了解释这里发生了什么:

  • SERIALIZABLE是最严格的隔离模式;它保证一次只有一个数据库事务可以执行这些语句,这是我们需要的,以保证这个“序列”的完整性。请注意,这会不可逆转地促进整个事务,因此您不希望在长时间运行的事务中使用此事务。

  • CTE获取已用于每个父ID的最高数字;

  • ROW_NUMBER为每个父ID(PARTITION BY)生成一个唯一的序列,从数字1开始;如果有一个获得新序列,我们将其添加到之前的最大值。

我可能还应该提到,如果您一次只需要插入一个新的子实体,那么最好只是通过存储过程而不是使用触发器来汇集这些操作 - 您肯定会获得更好的性能出来的。这就是SQL '08中hierarchyid列的当前完成情况。

答案 1 :(得分:2)

需要添加OUTPUT子句来触发Linq to SQL的兼容性。

例如:

INSERT MyTable (ParentEntityID, Number)
OUTPUT inserted.* 
SELECT
  i.ParentEntityID,
  ROW_NUMBER() OVER
  (
   PARTITION BY i.ParentEntityID
   ORDER BY (SELECT 1)
  ) + ISNULL(m.Number, 0) AS Number
FROM inserted i
LEFT JOIN MaxNumbers_CTE m
ON m.ParentEntityID = i.ParentEntityID

答案 2 :(得分:0)

这解决了我理解的问题: - )

DECLARE @foreignKey int
SET @foreignKey = 1  -- or however you get this

INSERT Tbl (ParentEntityId, Number) 
VALUES (@foreignKey, ISNULL((SELECT MAX(Number) FROM Tbl WHERE ParentEntityId = @foreignKey), 0) + 1)