具有包含Select的函数的约束永远不会满足

时间:2020-02-17 06:25:39

标签: sql sql-server tsql stored-functions check-constraints

我有一张这样的桌子:

CREATE TABLE CarRentSchedule
(
    carId INTEGER NOT NULL,
    rent_start INTEGER NOT NULL,
    rent_end INTEGER NOT NULL,
    personId INTEGER NOT NULL,
    tariff INTEGER NOT NULL,
    PRIMARY KEY carId,
    FOREIGN KEY (personId) REFERENCES Persons(PersonID)
    CONSTRAINT CHK_CR_AVLBL CHECK (dbo.CarCheck(carId , rent_start , rent_end ) = 1))

我需要禁止同一辆车的任何租赁时间交叉点。 我发现,当在约束内使用一个函数时(下面的示例),它返回的值与在约束外使用时的返回值不同。

if exists (select * from dbo.CarRentSchedule rt
               where rt.carId = @id 
                 and ((rt.rent_Start >= @startTime and rt.rent_end < @endTime)
                      or  
                      (rt.rent_end <= @endTime and rt.rent_end > @startTime)
                      or 
                      (rt.rent_Start < @endTime and rt.rent_end > @startTime)
                      or 
                      (rt.rent_Start >= @startTime and rt.rent_end <= @endTime)))
        return 0

因此,当我尝试将数据插入表中时,由于某种原因,约束永远不允许我这样做。

我已经检查过,从约束中调用时,该函数似乎返回NULL,但是像

这样调用该函数
select dbo.CheckFunc(1, 10, 15, 1, 1) 

将按预期返回0/1。我还检查了一下,发现如果函数中没有SELECT(例如,一个简单的比较操作),它在约束中也可以正常工作。

有人可以解释为什么这样工作吗?是使用约束内部的SELECT禁止某些约束,还是因为SELECT引用了约束所应用于的同一张表?

3 个答案:

答案 0 :(得分:0)

如果可以的话,您可以尝试以下方法:

Alter function AvailableCar
    (@car integer, @startTime integer, @endTime integer)
returns integer 
as
begin
declare @value int
    set @value = (select max(1) from dbo.CarRentSchedule as rt
               where rt.roomId = @car 
                 and ((rt.rent_Start >= @startTime and p.occ_start < @endTime)
                      or  
                      (rt.rent_end <= @endTime and rt.rent_end > @startTime)
                      or 
                      (rt.rent_Start < @endTime and rt.rent_end > @startTime)
                      or 
                      (rt.rent_Start >= @startTime and rt.rent_end <= @endTime)))
       if (@value is NULL)
       set @value = 0

       return @value


end

答案 1 :(得分:0)

这回答了问题的原始版本。

您的第一个条件应使用check约束进行处理:

check (occ_end > occ_start)

然后,正确的重叠逻辑可能会有所帮助。我建议:

if exists (select 1
           from dbo.RoomSchedule rs
           where p.roomId = @rn and
                 p.occ_end >= @startTime and 
                 p.occ_start < @endTime
          )

如果第一个在第二个结束之前开始,而第一个在第一个开始之后结束,则两个时间段重叠。

答案 2 :(得分:0)

我的猜测是,在检查第二个约束时,新的enty已被视为表的一部分,因此它始终与自身重叠。但这似乎很艰辛,不是吗?

是的,看起来是这样,我重新编写了该函数,以便它不会考虑新添加的条目,并且现在可以正常工作。奇怪,所以,我只是无法想象当前正在测试要添加的条目会参与该表的侧面选择。