2013-05-29 3 views
0

У меня есть таблица с 10 столбцами, но для этого нужны только 3 столбца. Представьте мой стол выглядит следующим образом:Сложное многостоечное уникальное ограничение

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

Что мне нужно, это ограничение, которое навязывает следующее: UserID и Идентификатор роли должны быть уникальными для каждого NodeID (т.е. пользователь не может иметь такую ​​же роль в нескольких узлах). Другими словами, я хочу, чтобы

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

, но не позволяют

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

если первая вставка произошло потому, что приведет к тому, что пользователь, имеющий роль в нескольких узлах.

Надеюсь, это просто, и я просто делаю его более сложным, чем это должно быть в моем мозгу.

+0

Что касается INSERT MyTable (UserID, NodeID, RoleID) SELECT 1, 2, 2? – bummi

+0

Я в порядке с пользователем, имеющим две разные роли в двух разных узлах, так что все будет хорошо. Хороший вопрос. :) – influent

+0

Тогда вы не ищете уникальную идентификатор UserID + RoleID? – kgu87

ответ

1

Поскольку ваше ограничение зависит от данных в других строках, это исключает фильтрованный индекс. ИМО жизнеспособным вариантом может быть триггер. Такой триггер может выглядеть примерно так:

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 
+0

Отличный ответ. – influent

1

Уникальный индекс должен обеспечить соблюдение ваших требований

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

Из комментариев, которые я полагаю, вы будете нуждаться в два уникальных индексах

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 
) 
+0

Это неправильно, возможно, я не объяснил это достаточно хорошо. Ваше решение позволяет, например, 1,1,1 только один раз, что хорошо, но он все еще позволяет 1,2,1. – influent

+0

Я не уверен, что именно должно быть уникальным, ИМХО вам просто нужно было бы пропустить одно из полей из индекса, скорее всего, RoleID? Если нет, то какие поля занимают 1,2,1? – bummi

+0

Я обновил свой вопрос, надеюсь, что это объяснит. – influent

Смежные вопросы