web-dev-qa-db-ja.com

日付範囲の比較

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
110
Kieran Benton

これは古典的な問題であり、実際に論理を逆にすると簡単です。

例を挙げましょう。

ここに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)

つまり、テスト範囲の開始がデータベース範囲の終了前であり、テスト範囲の終了がデータベース範囲の開始後または開始であることを確認します。

8
Paul Dixon

RDBMSがOVERLAP()関数をサポートしている場合、これは簡単になります-独自のソリューションは必要ありません。 (Oracleでは明らかに動作しますが、文書化されていません)。

4
David Aldridge

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 ;
0
jonavon

次の例を見てください。あなたの役に立つでしょう。

    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
0

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];
0
RickyS
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;
0
Paul Williamson

あなたの期待する結果であなたは言う

1983年6月6日から1983年6月18日=リスト中

ただし、この期間には、期間のテーブル(リストではない)の期間が含まれておらず、含まれていません。ただし、1983年6月10日から1983年6月14日までの期間と重複しています。

Snodgrassの本( http://www.cs.arizona.edu/people/rts/tdbbook.pdf )が役立つ場合があります。mysqlより前の日付ですが、時間の概念は変更されていません;-)

0
onedaywhen