您可以使用约束和触发器来完成此操作,因此无论您如何插入或更新数据,数据库都会执行您的逻辑。
CREATE TABLE dbo.Reservation(
Id int IDENTITY(1,1) NOT NULL,
VehicleId int NOT NULL,
StartDate datetime NOT NULL,
EndDate datetime NULL,
CONSTRAINT pk_dbo_reservation PRIMARY KEY NONCLUSTERED (Id ASC),
CONSTRAINT uq_dbo_reservation UNIQUE CLUSTERED (VehicleId ASC, StartDate ASC)
)
GO
ALTER TABLE dbo.Reservation WITH CHECK ADD CONSTRAINT ck_dbo_reservation_startdate_before_enddate CHECK (StartDate < EndDate)
GO
ALTER TABLE dbo.Reservation ADD CONSTRAINT df_dbo_reservation_startdate DEFAULT (GETDATE()) FOR StartDate
GO
---------------------------------------
CREATE TRIGGER dbo.trg_insupd_ReservationNoOverlap ON dbo.Reservation
AFTER INSERT, UPDATE
AS
SET NOCOUNT ON
IF EXISTS (
-- reservation is already active during the new time frame
SELECT b.VehicleId
FROM dbo.Reservation b
INNER JOIN inserted ins
ON b.VehicleId = ins.VehicleId
AND b.Id <> ins.Id
WHERE ( b.StartDate <= ins.StartDate
AND ( b.EndDate IS NULL
OR b.EndDate > ins.StartDate ) )
OR ( b.StartDate > ins.StartDate
AND ( ins.EndDate IS NULL
OR ins.EndDate > b.StartDate ) )
)
BEGIN
RAISERROR ('Dates cannot overlap for vehicle reservation.', 16, 1) WITH SETERROR;
ROLLBACK TRANSACTION;
RETURN
END
GO
测试用例:
INSERT INTO dbo.Reservation (VehicleId, StartDate, EndDate) VALUES (1, '12/01/2011', '12/06/2011') -- ok
INSERT INTO dbo.Reservation (VehicleId, StartDate, EndDate) VALUES (1, '12/06/2011', '12/08/2011') -- ok
INSERT INTO dbo.Reservation (VehicleId, StartDate, EndDate) VALUES (1, '12/03/2011', '12/04/2011') -- not ok (overlaps)
INSERT INTO dbo.Reservation (VehicleId, StartDate, EndDate) VALUES (1, '12/08/2011', NULL) -- ok (open-ended)
INSERT INTO dbo.Reservation (VehicleId, StartDate, EndDate) VALUES (1, '11/20/2011', '11/26/2011') -- ok
INSERT INTO dbo.Reservation (VehicleId, StartDate, EndDate) VALUES (1, '11/28/2011', NULL) -- not ok (overlaps)