sourcecode

MySQL에서 날짜 범위의 중복 확인

copyscript 2022. 9. 22. 00:12
반응형

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

그것을 얻을 수 있는 더 좋은 방법이 있을까요?


만지작거리다

예전에 작성한 캘린더 어플리케이션에 대한 문의가 있었습니다.이런 걸 썼던 것 같아요

... 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 - e - ne :중복 및 일치
  4. es - e - ns - ne :가 겹치지 않고 일치하지 않습니다(ns > e이기 때문입니다).
  5. es - ns - ne - ee :중복 및 일치
  6. ns - es - e - ne :중복 및 일치

여기 바이올린이 있습니다.

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개의 중복되는 경우에 대해 설명합니다.

Lamy의 답변은 좋지만, 조금 더 최적화할 수 있습니다.

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

그러면 범위가 겹치는 네 가지 시나리오가 모두 포착되고 그렇지 않은 두 가지 시나리오가 제외됩니다.

나도 비슷한 문제에 직면했었다.문제는 차단된 날짜 사이에 예약을 중지하는 것이었습니다.예를 들어 5월 2일부터 7일까지 숙박업소에 대한 예약이 차단되었습니다.예약을 감지하고 중지하려면 겹치는 날짜를 찾아야 했습니다.내 솔루션은 Lord Javac과 유사합니다.

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

안 되면 알려주세요.

(s1, e1) 및 (s2, e2)와 같이 s1 <e1 및 s2 <e2>와 같은2개의 인터벌이 주어집니다.
중복은 다음과 같이 계산할 수 있습니다.

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 

한 인터벌이 다른 인터벌 내에 있는 경우에도 동작합니다.

최근 같은 문제로 고민하다가 이 간단한 절차로 끝납니다(이것은 좋은 접근법이나 메모리 소비는 아닐 수 있습니다).

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)
);

이렇게 하면 날짜 범위가 겹치는 항목을 찾을 수 있습니다.

이게 도움이 됐으면 좋겠네요.

다음과 같이 데이터베이스의 현재 날짜가 null인 경우에도 날짜가 겹치는 모든 경우를 커버할 수 있습니다.

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

이렇게 하면 의 새 시작/종료 날짜와 겹치는 모든 레코드가 반환됩니다.

위의 맥크라켄의 답변은 두 날짜가 겹치는지 평가하기 위해 여러 OR을 필요로 하지 않기 때문에 성능 측면에서 더 좋습니다.좋은 해결책!

하지만 MySQL에서는 마이너스 연산자 대신 DATIFF를 사용해야 합니다.

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;

언급URL : https://stackoverflow.com/questions/2545947/check-overlap-of-date-ranges-in-mysql

반응형