2016-11-28 45 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上的索引表现不佳,请考虑对最常用的唯一列集编制索引。