假设该表有两列:
ParentEntityId int foreign key
Number int
ParentEntityId
是另一个表的外键。
Number
是本地标识,即它在单ParentEntityId
内是唯一的。
通过这两列上的唯一键可以轻松实现唯一性。
如何使Number
在插入ParentEntityId
的上下文中自动递增?
附录1
为澄清问题,这是一个摘要。
ParentEntity
有多个ChildEntity
,每个ChiildEntity
在其Number
的上下文中应具有唯一的增量ParentEntity
。
附录2
将ParentEntity
视为客户。
将ChildEntity
视为订单。
因此,每个客户的订单应编号为1,2,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)