web-dev-qa-db-ja.com

日付範囲の重複チェック制約

SQL Server 2005には、DateStart、DateEnd、Valueの3つの列を持つ単純なテーブルがあります。重複するレコードが挿入されないように、「テーブルチェック制約」を設定しようとしました。たとえば、そのようなテーブルにDateStart = 2012-01-01(1月1日)およびDateEnd 2012-01-15(1月15日)のレコードがある場合、チェック制約はDateStart = 2012-01-10(1月15日)のレコードの挿入を回避する必要があります。ケアなしDateEnd)、DateEnd = 2012-01-10(ケアなしDateStart)のレコード、またはDateStart2011-12-10およびDateEnd2012-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は範囲から除外されると思います。

22
Antonio Manello

CHECKが実行されていますafter行が挿入されたため、範囲がそれ自体と重複しています。

WHEREを修正して、@MyTableId <> MyTableIdのようなものを含める必要があります。


ところで、WHERE式は単純化できます。

次の場合、範囲は重複しません

  • 一方の範囲の終わりは、もう一方の開始の前です
  • または一方の範囲の開始はもう一方の終了の後にあります。

これは次のようにSQLで記述できます。

WHERE @DateEnd < DateStart OR DateEnd < @DateStart

do重なり合う範囲を取得するにはそれを否定します。

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フィドル]

注:範囲を「タッチ」できるようにするには、開始式で<=を使用します。これにより、最終式で「>」が生成されます。

31