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


82

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

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-052010-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 |
+----+------------+------------+

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


フィドル


1
3番目の条件が間違っています。あるはず"2010-01-05" <= start_date AND "2010-01-25" >= end_dateです。視覚化については、stackoverflow.com / a / 28802972/632951を参照してください。最初の(および2番目の)条件はすでにそれをカバーしているため、現在の3番目の条件は評価されません。
ペースリエ2015

回答:


156

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

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

更新これは間違いなく機能するはずです((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:重複して一致する

これがフィドルです


7
うまく機能します!しかし、@ Pierre deLESPINAYは彼のクエリで包括的範囲を探していると思います。
Osvaldo Mercado 2012年

16
@OsvaldoM。もし彼が本当にそうだったら、彼は約2年前に不平を言っていただろう…
soulmerge 2012年

私は今日イベントが終了し、別のイベントが今日開始します、それらは重複していますか?私の意見では、それらは重複します。しかし、SQLクエリはそれを言いません:sqlfiddle.com
AL

2
@soulmerge、実際には、彼は=それについて文句を言うのではなく、実際のコードにを追加しただけでした。
ペースリエ2015

32
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')

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


7
OPがこの重複する定義を明らかに探していなかったとしても、この回答は質問名で説明されている問題の最善の解決策です。私はこのオーバーラップを探していました。それが本当のオーバーラップです。
cec 2016年

2
「真のオーバーラップ」とはどういう意味かわかりませんが、@ soulmergeによるトップの回答はLamyによるこの回答と同等です。私は、等価性を証明するためにZ3定理ソルバーを使用することができました:grantjenks.com/projects/equivalent-inequalities
GrantJ

16

ラミーの答えは良いですが、もう少し最適化することができます。

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

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


これと上記の他の2つ以外に他の解決策はありますか?
Pacerier 2015

4

私も同様の問題に直面していました。私の問題は、ブロックされた日付の範囲の間に予約を停止することでした。たとえば、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

うまくいかない場合はお知らせください。


2

(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つの間隔が他の間隔内にある場合にも機能します。


0

最近、私は同じ問題に苦しんでいて、この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

次のように、データベースの日付がnullになる可能性がある場合でも、日付が重複するすべてのケースをカバーできます。

SELECT * FROM `tableName` t
WHERE t.`startDate` <= $toDate
AND (t.`endDate` IS NULL OR t.`endDate` >= $startDate);

これにより、とにかく新しい開始日/終了日と重複するすべてのレコードが返されます。


0

上記のMackrakenの回答は、2つの日付が重複しているかどうかを評価するために複数のORを必要としないため、パフォーマンスの観点から優れています。いい解決策!

ただし、MySQLでは、マイナス演算子の代わりにDATEDIFFを使用する必要があることがわかりました-

SELECT o.orderStart, o.orderEnd, s.startDate, s.endDate
, GREATEST(LEAST(orderEnd, endDate) - GREATEST(orderStart, startDate), 0)>0 as overlaps
, DATEDIFF(LEAST(orderEnd, endDate), GREATEST(orderStart, startDate)) as overlap_length
FROM orders o
JOIN dates s USING (customerId)
WHERE 1
AND DATEDIFF(LEAST(orderEnd, endDate),GREATEST(orderStart, startDate)) > 0;
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.