27

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は範囲から除外されると思います。

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

私は現在そのようなシナリオを持っているので、DateEndがnullである場合をBrankoDimitrijevic回答に追加したいと思います。

これは、ログにパンチを保持しているときにユーザーがまだログインしている場合に発生する可能性があります。

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 に答える