web-dev-qa-db-ja.com

MySQLで日付範囲の重複を確認する

このテーブルはセッション(イベント)を保存するために使用されます:

CREATE TABLE session (
  id int(11) NOT NULL AUTO_INCREMENT
, start_date date
, end_date date
);

INSERT INTO session
  (start_date, end_date)
VALUES
  ("2010-01-01", "2010-01-10")
, ("2010-01-20", "2010-01-30")
, ("2010-02-01", "2010-02-15")
;

範囲間で競合が発生することは望ましくありません。
新しいセッションを2010-01-05から2010-に挿入する必要があるとしましょう01-25
競合するセッションを知りたい。

私のクエリは次のとおりです。

SELECT *
FROM session
WHERE "2010-01-05" BETWEEN start_date AND end_date
   OR "2010-01-25" BETWEEN start_date AND end_date
   OR "2010-01-05" >= start_date AND "2010-01-25" <= end_date
;

結果は次のとおりです。

+----+------------+------------+
| id | start_date | end_date   |
+----+------------+------------+
|  1 | 2010-01-01 | 2010-01-10 |
|  2 | 2010-01-20 | 2010-01-30 |
+----+------------+------------+

それを取得するより良い方法はありますか?


フィドル

70

かつて書いたカレンダーアプリケーションでこのようなクエリがありました。私はこのようなものを使用したと思います:

... WHERE new_start < existing_end
      AND new_end   > existing_start;

[〜#〜] update [〜#〜]これは間違いなく動作するはずです((ns、ne、es、ee)=(new_start、new_end、existing_start 、existing_end)):

  1. ns-ne-es-ee:重複せず、一致しません(ne <esのため)
  2. ns-es-ne-ee:重複と一致
  3. es-ns-ee-ne:重複と一致
  4. es-ee-ns-ne:オーバーラップせず、一致しません(ns> eeのため)
  5. es-ns-ne-ee:重複と一致
  6. ns-es-ee-ne:重複と一致

フィドル

132
soulmerge
SELECT * FROM tbl WHERE
existing_start BETWEEN $newStart AND $newEnd OR 
existing_end BETWEEN $newStart AND $newEnd OR
$newStart BETWEEN existing_start AND existing_end

if (!empty($result))
throw new Exception('We have overlapping')

Sql句のこれらの3行は、必要な重複の4つのケースをカバーしています。

25
Lamy

Lamyの答えは良いのですが、もう少し最適化できます。

SELECT * FROM tbl WHERE
existing_start BETWEEN $newSTart AND $newEnd OR
$newStart BETWEEN existing_start AND existing_end

これにより、範囲が重複する4つのシナリオすべてがキャッチされ、重複しない2つのシナリオは除外されます。

13
LordJavac

私は同様の問題に直面していました。私の問題は、ブロックされた日付の範囲で予約を停止することでした。たとえば、5月2日から5月7日までの施設の予約はブロックされます。予約を検出して停止するには、重複する日付を見つける必要がありました。私のソリューションは、LordJavacに似ています。

SELECT * FROM ib_master_blocked_dates WHERE venue_id=$venue_id AND 
(
    (mbd_from_date BETWEEN '$from_date' AND '$to_date') 
    OR
    (mbd_to_date BETWEEN  '$from_date' AND '$to_date')
    OR
    ('$from_date' BETWEEN mbd_from_date AND mbd_to_date)
    OR      
    ('$to_date' BETWEEN mbd_from_date AND mbd_to_date)      
)
*mbd=master_blocked_dates

うまくいかない場合は教えてください。

4
Niraj Kumar

(s1、e1)と(s2、e2)のような2つの間隔が与えられ、s1 <e1およびs2 <e2
次のように重複を計算できます。

SELECT 
     s1, e1, s2, e2,
     ABS(e1-s1) as len1,
     ABS(e2-s2) as len2,
     GREATEST(LEAST(e1, e2) - GREATEST(s1, s2), 0)>0 as overlaps,
     GREATEST(LEAST(e1, e2) - GREATEST(s1, s2), 0) as overlap_length
FROM test_intervals 

一方の間隔がもう一方の間隔内にある場合にも機能します。

1
Mackraken

最近、私は同じ問題に苦労していて、この1つの簡単なステップで終了するようになりました(これは良いアプローチまたはメモリ消費ではないかもしれません)-

SELECT * FROM duty_register WHERE employee = '2' AND (
(
duty_start_date BETWEEN {$start_date} AND {$end_date}
OR
duty_end_date BETWEEN {$start_date} AND {$end_date}
)
OR
(
{$start_date} BETWEEN duty_start_date AND duty_end_date
OR
{$end_date} BETWEEN duty_start_date AND duty_end_date)
);

これにより、重複する日付範囲を持つエントリを見つけることができました。

これが誰かを助けることを願っています。

0