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は範囲から除外されると思います。
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
注:範囲を「タッチ」できるようにするには、開始式で<=
を使用します。これにより、最終式で「>」が生成されます。