2016-11-28 43 views
2

這是一個多租戶應用程序。所有記錄都有一個客戶端ID來分隔客戶端數據。客戶可以在此表中插入他們自己的數據並設置他們自己的唯一約束。每個客戶可以對15個字段中的任何一個設置唯一的約束,或者不設置任何約束所以,在實際的表上設置一個唯一的約束是行不通的。如何在多租戶數據庫中設置唯一約束條件

當前,爲了檢查是否應插入記錄,我們查詢數據庫以查看記錄是否存在。如果是這樣,我們不插入,否則我們做插入。如果在檢查和插入之間插入重複記錄,則重複項將泄漏到數據庫中。有沒有辦法保證重複不被插入?

+0

你考慮過鎖嗎? –

+0

@ PM77-1很好的建議。如果我對租戶ID進行鎖定,它是否會鎖定屬於租戶的所有記錄?如果是這樣,這是否會阻止來自其他會話的給定租戶ID插入記錄? – Luke101

+0

獨特的過濾索引(過濾「客戶端ID」)如何? http://sqlmag.com/blog/whats-good-use-unique-filtered-index – mendosi

回答

1

正如評論中所提到的,避免因爲並行運行的進程之間的時間問題而插入重複項的一種方法是將使用WHERE子句的行存在的測試與INSERT語句組合起來。 我建議動態SQL是一種可能的解決方案,但如果客戶端的約束設置存儲在數據庫中,則可以使用位掩碼替代方法,該方法可能適用於您。我做了一些假設,所以這可能無助於你。

請注意,此代碼被簡化爲僅與三列(而不是OP中提到的十五)一起工作。如果決定製作它,最好將邏輯包裝在存儲過程中。

-- run this code for different values of @ClientId and @DataN to test the behaviour 
DECLARE 
@ClientId int = 103, 
@Data1 int = 1, 
@Data2 int = 2, 
@Data3 int = 3 

DECLARE @clientConstraint TABLE (ClientId int PRIMARY KEY, Data1 bit, Data2 bit, Data3 bit) 
DECLARE @clientData TABLE (Id int IDENTITY PRIMARY KEY, ClientId int, Data1 int, Data2 int, Data3 int) 

-- set up four clients with different constraints for testing purposes 
INSERT @clientConstraint (ClientId, Data1, Data2, Data3) 
VALUES 
(100,0,0,0), 
(101,1,0,0), 
(102,0,1,0), 
(103,1,0,1) 

-- set up an existing row in the data table for each client 
INSERT @clientData (ClientId, Data1, Data2, Data3) 
VALUES 
(100,1,2,3), 
(101,1,2,3), 
(102,1,2,3), 
(103,1,2,3) 

-- build a bitmask of the client's unique columns 
DECLARE @ClientConstraintMask bigint = 0 
SELECT @ClientConstraintMask = Data1 + (Data2 * 2) + (Data3 * 4) 
FROM @clientConstraint 
WHERE ClientId = @ClientId 

-- insert the data, building a uniqueness bitmask and comparing to client's settings 
INSERT @clientData (ClientId, Data1, Data2, Data3) 
SELECT @ClientId,@Data1, @Data2, @Data3 
WHERE (SELECT 
     CASE WHEN c1.Data1 = @Data1 
       THEN @ClientConstraintMask & 1 
       ELSE 0 
     END + 
     CASE WHEN c1.Data2 = @Data2 
       THEN @ClientConstraintMask & 2 
       ELSE 0 
     END + 
     CASE WHEN c1.Data3 = @Data3 
       THEN @ClientConstraintMask & 4 
       ELSE 0 
     END 
     FROM @clientData AS c1 
     WHERE c1.ClientId = @ClientId 
    ) <> @ClientConstraintMask 

-- view the results 
SELECT * FROM @clientData 

它可能還值得一提的是,這取決於客戶端的數據量,您可能很難有效索引客戶端的數據表,以保持插入表現良好。如果只有ClientId上的索引表現不佳,請考慮對最常用的唯一列集編制索引。