2017-02-20 3 views
1

überlappt Ich habe folgende MySQL-Tabelle (Zimmer)Wie der Rest zwischen Datumsbereiche zu finden, die

 
id_user_room  username room_name date_from  date_to 
    1    mike  A1  2017-02-01  2017-02-10 
    2    evans  A2  2017-01-20  2017-01-30 
    3    mike  A3  2017-02-16  2017-02-20 
    4    mike  A1  2017-03-01  2017-03-18 

Wenn ich das Zimmer von Mike zwischen 2017.01.01 und 2017.05.30 wissen wollen , Führe ich die folgende SQL-Abfrage aus.

SELECT room_name, date_from, date_to 
FROM rooms 
WHERE (date_to >= '2017-01-01' AND date_from <= '2017-05-30') 
    AND username='Mike' 
order by date_to ASC 

Diese Abfrage ergibt folgendes Ergebnis:

 
room_name  date_from  date_to 
A1   2017-02-01  2017-02-10 
A3   2017-02-16  2017-02-20 
A1   2017-03-01  2017-03-18 

Gibt es eine Möglichkeit, die Ruhe (gegenüber) Datumsbereiche zu bekommen? Ich meine, ich würde gerne die Bereiche kennen, in denen Mike nicht in einem Raum lebte. Für das spezifische Beispiel würde das Ergebnis, das ich erhalten möchte, die folgenden Zeiträume sein:

Vielen Dank für Ihre Hilfe !!!

+1

Warum nicht in Ihrem PHP? –

+0

Wie man es mit PHP macht, hast du eine Idee? – George

+0

Erstellen Sie eine Tabelle mit allen Datumsangaben in einem Jahr, machen Sie dann Linksverbindungen, um Daten zu finden, und suchen Sie dann im Ergebnis Unterbrechungen mit einem Self-Join auf dem Ergebnis. Messy, but creative :) – ceteras

Antwort

2
DROP TABLE IF EXISTS my_table; 

CREATE TABLE my_table 
(username VARCHAR(12) NOT NULL 
,room_name CHAR(2) NOT NULL 
,date_from DATE 
,date_to DATE 
,PRIMARY KEY (username,room_name,date_from) 
); 

INSERT INTO my_table VALUES 
('mike','A1','2017-02-01','2017-02-10'), 
('evans','A2','2017-01-20','2017-01-30'), 
('mike','A3','2017-02-16','2017-02-20'), 
('mike','A1','2017-03-01','2017-03-18'); 

    SELECT username 
      , '2017-01-01' date_from 
      , MIN(date_from - INTERVAL 1 DAY) date_to 
     FROM my_table 
     GROUP 
     BY username 
     UNION 
    SELECT x.username 
     , x.date_to + INTERVAL 1 DAY 
     , MIN(y.date_from - INTERVAL 1 DAY) 
     FROM my_table x 
     JOIN my_table y ON y.date_to > x.date_to 
     AND y.username = x.username 
     GROUP 
     BY x.username 
      , x.date_to 
     UNION 
    SELECT username 
      , MAX(date_to + INTERVAL 1 DAY) 
      , '2017-05-30' 
     FROM my_table 
     GROUP 
     BY username; 
+----------+------------+------------+ 
| username | date_from | date_to | 
+----------+------------+------------+ 
| evans | 2017-01-01 | 2017-01-19 | 
| mike  | 2017-01-01 | 2017-01-31 | 
| mike  | 2017-02-11 | 2017-02-15 | 
| mike  | 2017-02-21 | 2017-02-28 | 
| evans | 2017-01-31 | 2017-05-30 | 
| mike  | 2017-03-19 | 2017-05-30 | 
+----------+------------+------------+ 
6 rows in set (0.01 sec) 

Dies setzt voraus, dass alle Daten innerhalb der Schwelle fallen - aber es ist nicht eine besonders komplizierte zwicken, wenn sie es nicht tun.

+0

Erdbeere, DANKE viel für Ihre Lösung! Es gibt mir genau das, was ich will, auf eine effiziente Art und Weise !!!! – George

0

Erstellen der Zimmer Tabelle, wie oben beschrieben:

CREATE TABLE rooms (
    id_user_room INTEGER, 
    username  CHAR(10), 
    room_name CHAR(10), 
    date_from TIMESTAMP, 
    date_to  TIMESTAMP 
); 

INSERT INTO rooms (id_user_room, username, room_name, date_from, date_to) VALUES 
(1, 'mike', 'A1', '2017-02-01', '2017-02-10'), 
(2, 'evans', 'A2', '2017-01-20', '2017-01-30'), 
(3, 'mike', 'A3', '2017-02-16', '2017-02-20'), 
(4, 'mike', 'A1', '2017-03-01', '2017-03-18') 
; 

Sie haben eine auxilary Datum Tisch, so etwas zu schaffen:

CREATE TABLE dates (
    day  TIMESTAMP 
); 

INSERT INTO dates (day) VALUES 
('2017-01-01'), 
('2017-01-02'), 
('2017-01-03'), 
... 
('2017-12-29'), 
('2017-12-30'), 
('2017-12-31') 
; 

diese gemeinsam mit Ihnen die Daten, an denen Mike war finden wohnt nicht in einem Raum:

CREATE TABLE empty_dates AS 
SELECT 
    d.day 
FROM 
    dates d 
LEFT JOIN 
    rooms r ON r.username = 'mike' AND d.day BETWEEN r.date_from AND r.date_to 
WHERE 
    r.id_user_room IS NULL 
; 

Der Kürze halber materialisierte ich diese Abfrage i anstelle von Unterabfragen. In anderen SQL-Sprachen wie TSQL oder PostgreSQL würde ich lieber CTE verwenden. Jetzt können Sie das Anfangs- und Enddatum der Intervalle angeben, indem Sie diese Tabelle mit sich selbst verbinden.

SELECT 
    CASE WHEN ed3.day IS NULL THEN 'Start date' ELSE 'End date' END row_type, 
    ed1.day 
FROM 
    empty_dates ed1 
LEFT JOIN 
    empty_dates ed2 ON DATEDIFF(ed2.day, ed1.day) = 1 
LEFT JOIN 
    empty_dates ed3 ON DATEDIFF(ed3.day, ed1.day) = -1 
WHERE 
    -- the next date is in the rooms table 
    ed2.day IS NULL 
    -- the previous date is in the rooms table 
    OR ed3.day IS NULL 
; 
Verwandte Themen