我在sql server 2005中有一个简单的表,有3列:DateStart,DateEnd和Value。我试图设置“表检查约束”以避免插入重叠记录。例如,如果在此表中有DateStart = 2012-01-01(第一月1月)和DateEnd 2012-01-15(1月15日)的记录,则Check约束必须避免插入DateStart = 2012-01-10的记录(无关心DateEnd),DateEnd = 2012-01-10(无关日期启动)的记录或DateStart 2011-12-10和DateEnd 2012-02-01的记录。
我以这种方式定义了一个UDF:
CREATE FUNCTION [dbo].[ufn_checkOverlappingDateRange]
(
@DateStart AS DATETIME
,@DateEnd AS DATETIME
)
RETURNS BIT
AS
BEGIN
DECLARE @retval BIT
/* date range at least one day */
IF (DATEDIFF(day,@DateStart,@DateEnd) < 1)
BEGIN
SET @retval=0
END
ELSE
BEGIN
IF EXISTS
(
SELECT
*
FROM [dbo].[myTable]
WHERE
((DateStart <= @DateStart) AND (DateEnd > @DateStart))
OR
((@DateStart <= DateStart) AND (@DateEnd > DateStart))
)
BEGIN
SET @retval=0
END
ELSE
BEGIN
SET @retval=1
END
END
RETURN @retval
END
然后考虑检查可能是这样的:
ALTER TABLE [dbo].[myTable] WITH CHECK ADD CONSTRAINT [CK_OverlappingDateRange] CHECK ([dbo].[ufn_checkOverlappingDateRange]([DateStart],[DateEnd])<>(0))
但即使使用[myTable]为空,EXISTS运算符在插入第一条记录时返回true。我在哪里wrog?是否可以设置这样的约束?
BTW我认为DateStart包含在范围内,而DateEnd包括在范围内。
答案 0 :(得分:29)
在插入行后,正在执行CHECK,因此范围与自身重叠。
您需要修改您的WHERE以包含以下内容:@MyTableId <> MyTableId
。
BTW,您的WHERE表达式可以简化。
如果符合以下情况,范围不会重叠:
可以用SQL编写:
WHERE @DateEnd < DateStart OR DateEnd < @DateStart
否定以获取执行重叠的范围...
WHERE NOT (@DateEnd < DateStart OR DateEnd < @DateStart)
...根据De Morgan's laws与...相同
WHERE (NOT (@DateEnd < DateStart) AND NOT (DateEnd < @DateStart))
......与以下内容相同:
WHERE @DateEnd >= DateStart AND DateEnd >= @DateStart
所以你最后的WHERE应该是:
WHERE
@MyTableId <> MyTableId
AND @DateEnd >= DateStart
AND DateEnd >= @DateStart
注意:要允许范围“触摸”,请在起始表达式中使用<=
,这会产生“&gt;”在最后的表达中。
答案 1 :(得分:-1)
我想在Branko Dimitrijevic的答案中添加DateEnd为空的情况,因为我目前有这种情况。
当您在日志中保持打卡并且用户仍然登录时,可能会发生这种情况。
WHERE
@MyTableId <> MyTableId
AND @DateEnd >= DateStart
AND DateEnd >= @DateStart
OR @DateEnd >= DateStart
AND DateEnd is null
OR @DateStart >= DateStart
AND DateEnd is null
我不知道这个查询在性能方面有多好,我确信有很多方法可以优化它。