MySQLで、日付範囲(範囲開始と範囲終了)のリストがある場合。例えば.
10/06/1983 to 14/06/1983
15/07/1983 to 16/07/1983
18/07/1983 to 18/07/1983
また、別の日付範囲に既にリストにある範囲が含まれているかどうかを確認したいのですが、どうすればよいですか?
例えば.
06/06/1983 to 18/06/1983 = IN LIST
10/06/1983 to 11/06/1983 = IN LIST
14/07/1983 to 14/07/1983 = NOT IN LIST
これは古典的な問題であり、実際に論理を逆にすると簡単です。
例を挙げましょう。
ここに1つの期間と、何らかの形で重複する他の期間のさまざまなバリエーションを投稿します。
|-------------------| compare to this one
|---------| contained within
|----------| contained within, equal start
|-----------| contained within, equal end
|-------------------| contained within, equal start+end
|------------| not fully contained, overlaps start
|---------------| not fully contained, overlaps end
|-------------------------| overlaps start, bigger
|-----------------------| overlaps end, bigger
|------------------------------| overlaps entire period
一方、重複しないものはすべて投稿しましょう。
|-------------------| compare to this one
|---| ends before
|---| starts after
したがって、単純に比較を次のように減らします。
starts after end
ends before start
重複していないものをすべて見つけ、一致しない期間をすべて見つけます。
最終的なNOT IN LISTの例では、これらの2つのルールに一致することがわかります。
次の期間が範囲内か範囲外かを判断する必要があります。
|-------------|
|-------| equal end with start of comparison period
|-----| equal start with end of comparison period
テーブルにrange_endおよびrange_startという列がある場合、一致するすべての行を取得する簡単なSQLを次に示します。
SELECT *
FROM periods
WHERE NOT (range_start > @check_period_end
OR range_end < @check_period_start)
そこに[〜#〜] not [〜#〜]があることに注意してください。 2つの単純なルールはすべての一致しない行を見つけるため、単純なNOTは逆になります:一致しない行のいずれでもない場合、それは次のいずれかでなければなりません一致するもの。
ここに単純な反転ロジックを適用してNOTを削除すると、次のようになります。
SELECT *
FROM periods
WHERE range_start <= @check_period_end
AND range_end >= @check_period_start
1983/06/06から1983/06/18の範囲の例を取り上げ、範囲にstartおよびendと呼ばれる列があると仮定すると、次のような節を使用できます
where ('1983-06-06' <= end) and ('1983-06-18' >= start)
つまり、テスト範囲の開始がデータベース範囲の終了前であり、テスト範囲の終了がデータベース範囲の開始後または開始であることを確認します。
RDBMSがOVERLAP()関数をサポートしている場合、これは簡単になります-独自のソリューションは必要ありません。 (Oracleでは明らかに動作しますが、文書化されていません)。
MySQLでこの問題に対処する関数を作成しました。使用する前に日付を秒に変換するだけです。
DELIMITER ;;
CREATE FUNCTION overlap_interval(x INT,y INT,a INT,b INT)
RETURNS INTEGER DETERMINISTIC
BEGIN
DECLARE
overlap_amount INTEGER;
IF (((x <= a) AND (a < y)) OR ((x < b) AND (b <= y)) OR (a < x AND y < b)) THEN
IF (x < a) THEN
IF (y < b) THEN
SET overlap_amount = y - a;
ELSE
SET overlap_amount = b - a;
END IF;
ELSE
IF (y < b) THEN
SET overlap_amount = y - x;
ELSE
SET overlap_amount = b - x;
END IF;
END IF;
ELSE
SET overlap_amount = 0;
END IF;
RETURN overlap_amount;
END ;;
DELIMITER ;
次の例を見てください。あなたの役に立つでしょう。
SELECT DISTINCT RelatedTo,CAST(NotificationContent as nvarchar(max)) as NotificationContent,
ID,
Url,
NotificationPrefix,
NotificationDate
FROM NotificationMaster as nfm
inner join NotificationSettingsSubscriptionLog as nfl on nfm.NotificationDate between nfl.LastSubscribedDate and isnull(nfl.LastUnSubscribedDate,GETDATE())
where ID not in(SELECT NotificationID from removednotificationsmaster where Userid=@userid) and nfl.UserId = @userid and nfl.RelatedSettingColumn = RelatedTo
BETWEEN SQLステートメントを使用する別の方法
含まれている期間:
SELECT *
FROM periods
WHERE @check_period_start BETWEEN range_start AND range_end
AND @check_period_end BETWEEN range_start AND range_end
除外期間:
SELECT *
FROM periods
WHERE (@check_period_start NOT BETWEEN range_start AND range_end
OR @check_period_end NOT BETWEEN range_start AND range_end)
MS SQLでこれを試してください
WITH date_range (calc_date) AS (
SELECT DATEADD(DAY, DATEDIFF(DAY, 0, [ending date]) - DATEDIFF(DAY, [start date], [ending date]), 0)
UNION ALL SELECT DATEADD(DAY, 1, calc_date)
FROM date_range
WHERE DATEADD(DAY, 1, calc_date) <= [ending date])
SELECT P.[fieldstartdate], P.[fieldenddate]
FROM date_range R JOIN [yourBaseTable] P on Convert(date, R.calc_date) BETWEEN convert(date, P.[fieldstartdate]) and convert(date, P.[fieldenddate])
GROUP BY P.[fieldstartdate], P.[fieldenddate];
CREATE FUNCTION overlap_date(s DATE, e DATE, a DATE, b DATE)
RETURNS BOOLEAN DETERMINISTIC
RETURN s BETWEEN a AND b or e BETWEEN a and b or a BETWEEN s and e;
あなたの期待する結果であなたは言う
1983年6月6日から1983年6月18日=リスト中
ただし、この期間には、期間のテーブル(リストではない)の期間が含まれておらず、含まれていません。ただし、1983年6月10日から1983年6月14日までの期間と重複しています。
Snodgrassの本( http://www.cs.arizona.edu/people/rts/tdbbook.pdf )が役立つ場合があります。mysqlより前の日付ですが、時間の概念は変更されていません;-)