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

发布于 2024-08-20 04:25:00 字数 740 浏览 4 评论 0原文

假设该表有两列:

ParentEntityId int foreign key
Number int

ParentEntityId 是另一个表的外键。

Number 是一个本地身份,即它在单个ParentEntityId 中是唯一的。

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

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


附录 1

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

ParentEntity 具有多个 ChildEntity,并且每个 ChiildEntity 在其 上下文中应该有一个唯一的增量 Number父实体


附录 2

ParentEntity 视为客户

ChildEntity 视为 Order

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

Suppose the table with two columns:

ParentEntityId int foreign key
Number int

ParentEntityId is a foreign key to another table.

Number is a local identity, i.e. it is unique within single ParentEntityId.

Uniqueness is easily achieved via unique key over these two columns.

How to make Number be automatically incremented in the context of the ParentEntityId on insert?


Addendum 1

To clarify the problem, here is an abstract.

ParentEntity has multiple ChildEntity, and each ChiildEntity should have an unique incremental Number in the context of its ParentEntity.


Addendum 2

Treat ParentEntity as a Customer.

Treat ChildEntity as an Order.

So, orders for every customer should be numbered 1, 2, 3 and so on.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

我是有多爱你 2024-08-27 04:25:00

好吧,这种类型的列没有本机支持,但您可以使用触发器来实现它:

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 列完成的方式。

Well, there's no native support for this type of column, but you could implement it using a trigger:

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

Not tested but I'm pretty sure it'll work. If you have a primary key, you could also implement this as an AFTER trigger (I dislike using INSTEAD OF triggers, they're harder to understand when you need to modify them 6 months later).

Just to explain what's going on here:

  • SERIALIZABLE is the strictest isolation mode; it guarantees that only one database transaction at a time can execute these statements, which we need in order to guarantee the integrity of this "sequence." Note that this irreversibly promotes the entire transaction, so you won't want to use this inside of a long-running transaction.

  • The CTE picks up the highest number already used for each parent ID;

  • ROW_NUMBER generates a unique sequence for each parent ID (PARTITION BY) starting from the number 1; we add this to the previous maximum if there is one to get the new sequence.

I probably should also mention that if you only ever need to insert one new child entity at a time, you're better off just funneling those operations through a stored procedure instead of using a trigger - you'll definitely get better performance out of it. This is how it's currently done with hierarchyid columns in SQL '08.

撩起发的微风 2024-08-27 04:25:00

需要添加 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

Need add OUTPUT clause to trigger for Linq to SQL сompatibility.

For example:

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
再可℃爱ぅ一点好了 2024-08-27 04:25:00

据我了解,这解决了问题::-)

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)

This solves the question as I understand it: :-)

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)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文