【问题标题】:Complex multi-column unique constraint复杂的多列唯一约束
【发布时间】:2013-05-25 08:00:33
【问题描述】:

我有一个有 10 列的表,但只关心 3 列。想象一下我的桌子是这样的:

CREATE TABLE MyTable ( RowID int IDENTITY(1,1), UserID int, NodeID int, RoleID int )

我需要的是一个强制执行以下内容的约束:每个 NodeID 的 UserID 和 RoleID 必须是唯一的(即,一个用户不能在多个节点中拥有相同的角色)。换句话说,我想允许

INSERT MyTable (UserID, NodeID, RoleID) SELECT 1, 1, 1

但不允许

INSERT MyTable (UserID, NodeID, RoleID) SELECT 1, 2, 1

如果第一次插入已经发生,因为这会导致用户在多个节点中拥有一个角色。

希望这很简单,我只是让它变得比我的大脑需要的更复杂。

【问题讨论】:

  • INSERT MyTable (UserID, NodeID, RoleID) SELECT 1, 2, 2 怎么样?
  • 我可以接受在两个不同节点中拥有两个不同角色的用户,这样就可以了。好问题。 :)
  • 那你不是在寻找唯一的 UserID + RoleID 吗?
  • 我也在寻找它,但仅限于每个 NodeID。

标签: sql-server sql-server-2008 unique-constraint


【解决方案1】:

由于您的约束取决于其他行中的数据,因此排除了过滤索引。 IMO 一个可行的选择可能是一个触发器。这样的触发器可能看起来像这样:

CREATE TRIGGER dbo.MyTrigger ON dbo.Q1
    AFTER INSERT, UPDATE
AS
    DECLARE @userId INT, @Id INT, @roleId INT, @exists INT;

    SELECT TOP 1
            @userId = userID
           ,@roleId = roleID
           ,@Id = Id
    FROM    inserted;    

    SELECT TOP 1
            @exists = Id
    FROM    Q1
    WHERE   userId = @userId
            AND roleID = @roleID AND Id<> @Id;    

    IF ISNULL(@exists, 0) > 0 
        BEGIN           
            -- you would want to either undo the action here when you use an 'after' trigger
            -- because as the name implies ... the after means the record is allready inserted/updated          
            RAISERROR ('No way we would allow this.', 16, 1);
        END
        -- else
        -- begin
            -- another alternative would be to use a instead of trigger, which means the record
            -- has not been inserted or updated and since that type of trigger runs the trigger 'instead of'
            -- updating or inserting the record you would need to do that yourself. Pick your poison ...
        -- end
GO

【讨论】:

    【解决方案2】:

    唯一索引应该满足您的要求

    CREATE UNIQUE NONCLUSTERED INDEX [idx_Unique] ON [dbo].[MyTable] 
    (
        [UserID] ASC,
        [NodeID] ASC,
        [RoleID] ASC
    )
    

    从 cmets 我想你需要两个唯一的索引

    CREATE UNIQUE NONCLUSTERED INDEX [idx_User_Node] ON [dbo].[MyTable] 
    (
        [UserID] ASC,
        [NodeID] ASC
    )
    GO
    CREATE UNIQUE NONCLUSTERED INDEX [idx_User_Role] ON [dbo].[MyTable] 
    (
        [UserID] ASC,
        [RoleID] ASC
    )
    

    【讨论】:

    • 那是不正确的,也许我解释得不够好。您的解决方案允许例如1,1,1 只有一次,这很好,但它仍然允许 1,2,1。
    • 我不知道什么应该是唯一的,恕我直言,您只需要跳过索引中的一个字段,很可能是 RoleID?如果不是,1、2、1后面的字段是什么?
    • 也不是这样。只要角色不同,一个用户就可以属于多个节点。 idx_User_Role 可能会有所帮助。谢谢。
    猜你喜欢
    • 1970-01-01
    • 2011-11-12
    • 2012-08-10
    • 2019-02-27
    • 2010-12-22
    • 1970-01-01
    • 2016-08-22
    • 2013-08-23
    相关资源
    最近更新 更多