it-swarm.com.de

Datumsbereiche vergleichen

Wenn ich in MySQL eine Liste von Datumsbereichen habe (Bereichsbeginn und -ende). z.B.

10/06/1983 to 14/06/1983
15/07/1983 to 16/07/1983
18/07/1983 to 18/07/1983

Und ich möchte überprüfen, ob ein anderer Datumsbereich einen der Bereiche enthält, die bereits in der Liste enthalten sind. Wie würde ich das tun?

z.B.

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

Dies ist ein klassisches Problem, und es ist tatsächlich einfacher, wenn Sie die Logik umkehren.

Lassen Sie mich Ihnen ein Beispiel geben.

Ich werde hier einen Zeitraum veröffentlichen und all die verschiedenen Variationen anderer Zeiträume, die sich auf irgendeine Weise überschneiden.

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

lassen Sie mich auf der anderen Seite all jene posten, die sich nicht überschneiden:

           |-------------------|          compare to this one
     |---|                                ends before
                                 |---|    starts after

Reduzieren Sie den Vergleich also einfach auf:

starts after end
ends before start

dann finden Sie alle diejenigen, die sich nicht überlappen, und dann finden Sie alle nicht übereinstimmenden Zeiträume.

In Ihrem letzten NOT IN LIST-Beispiel können Sie sehen, dass es diesen beiden Regeln entspricht.

Sie müssen entscheiden, ob die folgenden Zeiträume IN oder AUSSERHALB Ihrer Bereiche liegen:

           |-------------|
   |-------|                       equal end with start of comparison period
                         |-----|   equal start with end of comparison period

Wenn Ihre Tabelle Spalten mit den Namen range_end und range_start enthält, finden Sie hier einige einfache SQL-Anweisungen zum Abrufen aller übereinstimmenden Zeilen:

SELECT *
FROM periods
WHERE NOT (range_start > @check_period_end
           OR range_end < @check_period_start)

Beachten Sie das NICHT darin. Da die zwei einfachen Regeln alle nicht übereinstimmenden Zeilen finden, kehrt ein einfaches NOT diese um und sagt: wenn es nicht eine der nicht übereinstimmenden Zeilen ist, muss es eine von sein die passenden.

Wenn Sie hier eine einfache Umkehrlogik anwenden, um das NOT zu beseitigen, erhalten Sie:

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

Nehmen Sie den Beispielbereich 06/06/1983 bis 18/06/1983 und setzen Sie voraus, dass Sie Spalten mit den Namen start und end für Ihre Bereiche haben, und verwenden Sie eine Klausel wie diese

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

d.h. überprüfen Sie, ob der Beginn Ihres Testbereichs vor dem Ende des Datenbankbereichs liegt und ob das Ende Ihres Testbereichs nach oder am Anfang des Datenbankbereichs liegt.

8
Paul Dixon

Wenn Ihr RDBMS die OVERLAP () -Funktion unterstützt, ist dies trivial - es sind keine eigenen Lösungen erforderlich. (In Oracle funktioniert es anscheinend, ist aber nicht dokumentiert).

4
David Aldridge

Ich habe eine Funktion erstellt, um dieses Problem in MySQL zu lösen. Konvertieren Sie einfach die Daten vor der Verwendung in Sekunden.

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

Schauen Sie sich das folgende Beispiel an. Es wird für Sie hilfreich sein.

    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 [email protected]) and  nfl.UserId = @userid and nfl.RelatedSettingColumn = RelatedTo
0

Eine andere Methode unter Verwendung der SQL-Anweisung BETWEEN

Zeiträume enthalten:

SELECT *
FROM periods
WHERE @check_period_start BETWEEN range_start AND range_end
  AND @check_period_end BETWEEN range_start AND range_end

Ausgeschlossene Fristen:

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)

Versuchen Sie dies auf 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

In Ihren erwarteten Ergebnissen sagen Sie

06/06/1983 bis 18/06/1983 = IN LIST

Diese Periode enthält jedoch keine der Perioden in Ihrer Tabelle (nicht in der Liste!). Es überlappt jedoch den Zeitraum 10/06/1983 bis 14/06/1983.

Möglicherweise finden Sie das Snodgrass-Buch ( http://www.cs.arizona.edu/people/rts/tdbbook.pdf ) nützlich: Es datiert vor MySQL, aber das Konzept der Zeit hat sich nicht geändert ;-)

0
onedaywhen