27

我在 sql server 2005 中有一个包含 3 列的简单表:DateStart、DateEnd 和 Value。我尝试设置“表检查约束”以避免插入重叠记录。例如,如果在此类表中存在 DateStart = 2012-01-01(1 月 1 日)和 DateEnd 2012-01-15(1 月 15 日)的记录,则 Check 约束必须避免插入 DateStart=2012-01-10 的记录( no care DateEnd)、DateEnd=2012-01-10 (no care DateStart) 的记录或 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。我在哪里?是否可以设置这样的约束?

顺便说一句,我认为 DateStart 包括在范围内,而 DateEnd 不包括在范围内。

4

2 回答 2

37

插入行正在执行 CHECK ,因此范围与自身重叠。

您需要修改 WHERE 以包含以下内容:@MyTableId <> MyTableId.


顺便说一句,您的 WHERE 表达式可以简化。

如果出现以下情况,范围重叠:

  • 一个范围的结束在另一个范围的开始之前
  • 一个范围的开始在另一个范围的结束之后。

可以用 SQL 编写,例如:

WHERE @DateEnd < DateStart OR DateEnd < @DateStart

否定它以获得重叠的范围......

WHERE NOT (@DateEnd < DateStart OR DateEnd < @DateStart)

...根据德摩根定律,这与...相同

WHERE NOT (@DateEnd < DateStart) AND NOT (DateEnd < @DateStart)

...这与:

WHERE @DateEnd >= DateStart AND DateEnd >= @DateStart

所以你的最终 WHERE 应该是:

WHERE
    @MyTableId <> MyTableId
    AND @DateEnd >= DateStart
    AND DateEnd >= @DateStart

[SQL 小提琴]

注意:要允许范围“接触”,<=请在起始表达式中使用,这将>在最终表达式中产生。

于 2012-08-20T19:23:12.847 回答
-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

我不知道这个查询在性能方面有多好,我相信有办法优化它。

于 2016-12-08T20:58:22.540 回答