正如评论中提到的,避免由于并行运行的进程之间的计时问题而插入重复项的一种方法是将行是否存在的测试与INSERT
声明使用WHERE
条款。
我建议动态 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
单独执行还不够好。