选择并插入同一事务以确保唯一性

时间:2017-03-10 04:48:16

标签: sql sql-server

我们有预订解决方案,可以预订设施。我们必须确保设施不是双重预订。虽然到目前为止我们还没有任何双重预订,但我不相信当前的实施可以确保这一点。

如果有人可以提供最好的建议就是这样做,我们将不胜感激。我想要一个解决方案,避免做插入,然后不得不做另一次读取检查时间没有重叠。

编辑:因为看起来需要更多细节。在显示的查询中,我们执行SELECT,然后执行INSERT。我假设在SELECT(说时间可用)和INSERT(预订时间)之间有可能另一个用户可以进行INSERT,从而以双重预订结束。我假设有一些方法可以在事务中包装SELECT和INSERT(尽管我还不了解隔离级别是如何工作的)。另一个因素是这是一个多租户的解决方案,所以我担心如果在事务中包装SELECT和INSERT将需要一个表锁定,这将导致其他性能问题。

目前我们使用以下SQL进行插入。我们基本上做一个选择以查看时间是否可用,如果是,则执行插入,然后返回更改了多少行以查看插入是否成功。

INSERT INTO Bookings (TenantId, FacilityId, UnitId, StartDateTime, EndDateTime, ...)
SELECT @TenantId, @FacilityId, @UnitId, @StartDateTime, @EndDateTime, ...
WHERE (SELECT COUNT(*) FROM Bookings WHERE TenantId = @TenantId AND FacilityId = @FacilityId AND
    ((StartDateTime >= @StartDateTime AND StartDateTime < @EndDateTime) OR (EndDateTime > @StartDateTime AND StartDateTime < @StartDateTime))) = 0; SELECT @@Identity AS [Identity], @@RowCount AS [RowCount]";

2 个答案:

答案 0 :(得分:0)

我认为,使用子查询总是会减慢查询运行时间。 说实话,我不确定你要做什么,但我可以改变这样的查询。

INSERT INTO Bookings (TenantId, FacilityId, UnitId, StartDateTime, EndDateTime, ...)
SELECT @TenantId, @FacilityId, @UnitId, @StartDateTime, @EndDateTime, ...
LEFT JOIN Bookings b ON b.TenantId = @TenantId AND b.FacilityId = @FacilityId
WHERE 
(
 (StartDateTime >= @StartDateTime AND StartDateTime < @EndDateTime) OR (EndDateTime > @StartDateTime AND StartDateTime < @StartDateTime)
) AND b.TenantId IS NULL

但是,如果我理解正确,我将不会使用此代码,我将首先检查而不是直接使用插入查询。 例如,使用此选择查询而不是插入,如果它不为零,则不必运行插入查询。

SELECT COUNT(*) 
FROM Bookings 
WHERE 
    TenantId = @TenantId AND FacilityId = @FacilityId AND
    (
        (StartDateTime >= @StartDateTime AND StartDateTime < @EndDateTime) OR 
        (EndDateTime > @StartDateTime AND StartDateTime < @StartDateTime)
    )

答案 1 :(得分:0)

您可以使用NOT EXISTS进行验证。这比使用COUNT检查存在更快。

IF NOT EXISTS (SELECT * FROM dbo.Bookings as b WHERE b.FacilityID = @FacilityId 
                        AND ((StartDateTime >= @StartDateTime AND StartDateTime < @EndDateTime) OR (EndDateTime > @StartDateTime AND StartDateTime < @StartDateTime)) )
BEGIN
    INSERT dbo.Bookings (TenantId, FacilityId, UnitId, StartDateTime, EndDateTime, ...)
    SELECT  @TenantId, @FacilityId, @UnitId, @StartDateTime, @EndDateTime, ...;
END
ELSE
    PRINT   'Facility is already booked for this duration.';