기간 비교


116

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

답변:


439

이것은 고전적인 문제이며 논리를 반대로하면 실제로 더 쉽습니다.

예를 들어 보겠습니다.

여기에 한 기간을 게시하고 어떤 식 으로든 겹치는 다른 기간의 모든 변형을 게시하겠습니다.

           |-------------------|          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 예의 경우 두 규칙과 일치 함을 알 수 있습니다.

다음 기간이 범위 안 또는 밖인지 결정해야합니다.

           |-------------|
   |-------|                       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 에 유의하십시오 . 두 개의 간단한 규칙이 일치하지 않는 모든 행을 찾기 때문에 간단한 NOT은 다음과 같이 반전합니다. 일치하지 않는 행 중 하나가 아닌 경우 일치하는 행 중 하나 여야합니다 .

NOT을 제거하기 위해 여기에 간단한 반전 논리를 적용하면 다음과 같이 끝납니다.

SELECT *
FROM periods
WHERE range_start <= @check_period_end
      AND range_end >= @check_period_start

45
답변을 두 번 이상 찬성 할 수있는 "ACII 다이어그램 포함"플래그가 필요합니다
Jonny Buchanan

29
아마도 내가 본 5 개의 베스트 답변 중 하나 일 것입니다. 문제에 대한 훌륭한 설명, 솔루션의 멋진 연습 및 ... 사진!
davidavr

10
한 번 이상 투표 할 수 있다면 그렇게 할 것입니다. 발생하는 일반적인 문제에 대한 위대하고 명확하며 간결한 설명, 내가보기 드물게 잘 설명 된 해결책!
ConroyP

2
좋은 대답입니다! 내가 추가 할 유일한 것은-엔드 포인트가 포함되는지 여부를 결정하는 것과 관련하여-한 쪽은 닫힌 간격을, 다른 쪽은 열린 간격을 사용하면 모든 것이 더 깨끗하게 작동합니다. 예를 들어 범위의 시작은 지점에 포함되고 범위의 끝은 포함되지 않습니다. 특히 다양한 해상도의 날짜 및 시간 조합을 처리 할 때 모든 것이 더 간단 해집니다.
Eclipse

1
좋은 대답입니다. 이것은 또한 Allen의 Interval Algebra 라고도 합니다. 나는 비슷한 대답 을 가지고 있으며 한 해설자와 얼마나 많은 다른 비교가 있는지를 놓고 치열한 싸움을 벌였습니다.
Jonathan Leffler

8

1983 년 6 월 6 일부터 1983 년 6 월 18 일까지의 예제 범위를 사용하고 범위에 대해 시작 이라는 열이 있다고 가정하면 다음 과 같은 절을 사용할 수 있습니다.

where ('1983-06-06' <= end) and ('1983-06-18' >= start)

즉, 테스트 범위의 시작이 데이터베이스 범위의 끝 이전인지, 테스트 범위의 끝이 데이터베이스 범위의 시작 이후 또는 시작인지 확인하십시오.


4

RDBMS가 OVERLAP () 함수를 지원하는 경우 이는 사소 해집니다. 자체 개발 솔루션이 필요하지 않습니다. (오라클에서는 작동하지만 문서화되지 않았습니다).


1
에픽 솔루션. 잘 작동합니다. 이것은 Oracle에서 2 개의 날짜 범위 (s1, e1) 및 (s2, e2)에 대한 구문입니다.
ihebiheb

0

예상되는 결과에서

1983 년 6 월 6 일 ~ 1983 년 6 월 18 일 = 목록에 있음

그러나이 기간은 기간 표 (목록이 아님)에있는 기간을 포함하거나 포함하지 않습니다. 그러나 기간은 1983 년 10 월 6 일에서 1983 년 6 월 14 일까지입니다.

Snodgrass 책 ( http://www.cs.arizona.edu/people/rts/tdbbook.pdf )이 유용 할 수 있습니다.이 책 은 mysql보다 이전 버전이지만 시간 개념은 변경되지 않았습니다 ;-)


0

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

다음 예를 살펴보십시오. 당신에게 도움이 될 것입니다.

    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

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

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)

-2
SELECT * 
FROM tabla a 
WHERE ( @Fini <= a.dFechaFin AND @Ffin >= a.dFechaIni )
  AND ( (@Fini >= a.dFechaIni AND @Ffin <= a.dFechaFin) OR (@Fini >= a.dFechaIni AND @Ffin >= a.dFechaFin) OR (a.dFechaIni>=@Fini AND a.dFechaFin <=@Ffin) OR
(a.dFechaIni>=@Fini AND a.dFechaFin >=@Ffin) )

Stack Overflow에 오신 것을 환영합니다! 즉각적인 도움을 줄 수있는이 코드 스 니펫에 감사드립니다. 적절한 설명 이것이 문제에 대한 좋은 해결책 인 이유 를 보여줌으로써 교육적 가치를 크게 향상시키고 유사하지만 동일하지는 않은 질문을 가진 미래의 독자에게 더 유용하게 만들 것입니다. 제발 편집 설명을 추가하고, 제한 및 가정이 적용 무엇의 표시를 제공하는 답변을.
토비 Speight
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.