it-swarm.com.de

MySQL - Datums- / Zeitintervalle zusammenführen oder teilen (Startdatum bis Enddatum)

Ich habe eine Tabelle, in der eine Liste von Aktivitäten mit einem Zeitintervall gespeichert ist, das durch zwei Daten begrenzt ist.

Stichprobe:

+------+---------------------+---------------------+-------------+
| name |        start        |         end         | time (calc) |
+------+---------------------+---------------------+-------------+
|  me  | 2017-04-03 11:00:00 | 2017-04-03 11:30:00 |          30 |
|  me  | 2017-04-03 23:45:00 | 2017-04-04 00:15:00 |          30 |
|  me  | 2017-04-04 10:00:00 | 2017-04-04 11:00:00 |          60 |
|  me  | 2017-04-04 10:30:00 | 2017-04-04 11:30:00 |          60 |
|  me  | 2017-04-05 23:00:00 | 2017-04-05 23:30:00 |          30 |
|  me  | 2017-04-05 23:15:00 | 2017-04-07 00:45:00 |        1530 |
+------+---------------------+---------------------+-------------+

Ich möchte wissen, wie viele Minuten pro Tag (und dann pro Woche) pro Tag belegt sind, daher muss ich die aktuelle Tabelle so umwandeln, dass Intervalle, die eine Teilraumzeit gemeinsam haben, in einer einzigen zusammengeführt werden und Intervalle, die in mehreren liegen Die Tage sind wie beim nächsten aufgeteilt:

+------+---------------------+---------------------+-------------+
| name |        start        |         end         | time (calc) |
+------+---------------------+---------------------+-------------+
|  me  | 2017-04-03 11:00:00 | 2017-04-03 11:30:00 |          30 |
|  me  | 2017-04-03 23:45:00 | 2017-04-03 23:59:59 |          15 |
|  me  | 2017-04-04 00:00:00 | 2017-04-04 00:15:00 |          15 |
|  me  | 2017-04-04 10:00:00 | 2017-04-04 11:30:00 |          90 |
|  me  | 2017-04-05 23:00:00 | 2017-04-05 23:59:59 |          60 |
|  me  | 2017-04-06 00:00:00 | 2017-04-06 23:59:59 |        1440 |
|  me  | 2017-04-07 00:00:00 | 2017-04-07 00:45:00 |          45 |
+------+---------------------+---------------------+-------------+

Um es dann einfach abzufragen, um Minuten pro Tag zu erhalten:

+------+------------+------+
| name |    day     | time |
+------+------------+------+
|  me  | 2017-04-03 |   45 |
|  me  | 2017-04-04 |  105 |
|  me  | 2017-04-05 |   60 |
|  me  | 2017-04-06 | 1440 |
|  me  | 2017-04-07 |   45 |
+------+------------+------+

Ich habe nach Informationen gesucht und hier herausgefunden, wie mehrere Datumsintervalle zusammengeführt werden können ( mysql - Summenintervalldaten ), kann jedoch ein Intervall nicht in mehrere Tage aufteilen.

Ist es möglich, es in einer einzigen Abfrage zu tun? Wie könnte ich das machen?

Bearbeiten:

SQL (Struktur und Daten):

CREATE TABLE activities (
    id INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
    name VARCHAR(45),
    start DATETIME,
    end DATETIME,
    time INT GENERATED ALWAYS AS (TIMESTAMPDIFF(MINUTE, start, end)) VIRTUAL
);

INSERT INTO activities (name, start, end) VALUES
('me','2017-04-03 11:00','2017-04-03 11:30'),
('me','2017-04-03 23:45','2017-04-04 00:15'),
('me','2017-04-04 10:00','2017-04-04 11:00'),
('me','2017-04-04 10:30','2017-04-04 11:30'),
('me','2017-04-05 23:00','2017-04-05 23:30'),
('me','2017-04-05 23:15','2017-04-07 00:45');

SQL zum Zusammenführen mehrerer Intervalle (Soruce: mysql - Summenintervalldaten ):

SELECT name, min(start) AS start, end, TIMESTAMPDIFF(MINUTE, MIN(start), end) AS time
FROM (
    SELECT x.name, x.start, min(y.end) AS end 
    FROM activities AS x 
    JOIN activities AS y 
        ON x.name = y.name 
       AND x.start <= y.end 
       AND NOT EXISTS (
           SELECT 1 
           FROM activities AS z 
           WHERE y.name = z.name 
             AND y.end >= z.start 
             AND y.end < z.end
       ) 
    WHERE NOT EXISTS (
        SELECT 1 
        FROM activities AS u 
        WHERE x.name = u.name 
          AND x.start > u.start 
          AND x.start <= u.start
    ) 
    GROUP BY x.name, x.start
) AS v GROUP BY name, end;
5
vgc

Da Sie zuerst eine Reihe von Daten generieren müssen, würde ich vorschlagen, eine calendar -Tabelle zu verwenden.

CREATE TABLE if not exists calendar (
    mdate date PRIMARY KEY NOT NULL
);

INSERT INTO calendar values
('20170403'),('20170404'),('20170405'),('20170406'),('20170407'),('20170408');

Wie machen sie das

Um überlappende Aktivitäten zu erhalten, habe ich die Abfrage verwendet, die Sie für Ihre Frage angegeben haben.

create view overlaped_activities
as
SELECT name, min(start) AS start, end, TIMESTAMPDIFF(MINUTE, MIN(start), end) AS time
FROM (
    SELECT x.name, x.start, min(y.end) AS end 
    FROM activities AS x 
    JOIN activities AS y 
        ON x.name = y.name 
       AND x.start <= y.end 
       AND NOT EXISTS (
           SELECT 1 
           FROM activities AS z 
           WHERE y.name = z.name 
             AND y.end >= z.start 
             AND y.end < z.end
       ) 
    WHERE NOT EXISTS (
        SELECT 1 
        FROM activities AS u 
        WHERE x.name = u.name 
          AND x.start > u.start 
          AND x.start <= u.start
    ) 
    GROUP BY x.name, x.start
) AS v GROUP BY name, end;

Zuerst berechne ich die Minuten vom Startdatum bis Mitternacht:

if(date(start) = date(end), 
  time_to_sec(timediff(end, start)) / 60, 
  (1440 - time_to_sec(time(start)) / 60)) mstart

Wenn dann start <> end ist, berechne ich die Minuten von Mitternacht bis zum Enddatum:

if(date(start) = date(end), 0, time_to_sec(time(end)) / 60) mend

Dies gibt eine Tabelle wie folgt zurück:

| start               | end                 |     mdiff |  mstart |    mend |
|---------------------|---------------------|----------:|--------:|--------:|
| 03.04.2017 11:00:00 | 03.04.2017 11:30:00 |   30,0000 | 30,0000 |       0 |
| 03.04.2017 23:45:00 | 04.04.2017 00:15:00 |   30,0000 | 15,0000 | 15,0000 |
| 04.04.2017 10:00:00 | 04.04.2017 11:30:00 |   90,0000 | 90,0000 |       0 |
| 05.04.2017 23:00:00 | 07.04.2017 00:45:00 | 1545,0000 | 60,0000 | 45,0000 |

Das ist schön, aber hier gibt es ein anderes Problem:

| 05.04.2017 23:00:00 | 07.04.2017 00:45:00 | 1545,0000 | 60,0000 | 45,0000 |

Natürlich: 1545 <> 60 + 45

Wir müssen eine Reihe von Daten zwischen Start- und Enddatum generieren und jedem Tag 1440 Minuten hinzufügen.

Wir können es über die Kalendertabelle erhalten:

   select   name,
            mdate date_activity,
            sum(1440) minutes
   from     calendar
   join     overlaped_activities
   on       calendar.mdate > date(start)
   and      calendar.mdate < date(end)
   where    datediff(end, start) > 1
   group by name, mdate

Ok, wir haben alle Zutaten, es ist Zeit, das Rezept zu kochen:

select name, date_activity, sum(minutes) min_activity
from (
       select name, 
              date(start) date_activity,
              if(date(start) = date(end), time_to_sec(timediff(end, start)) / 60, (1440 - time_to_sec(time(start)) / 60)) minutes
       from overlaped_activities

       UNION ALL

       select name, 
              date(end) date_activity,
              if(date(start) = date(end), 0, time_to_sec(time(end)) / 60) minutes
       from overlaped_activities

       UNION ALL

       select   name,
                mdate date_activity,
                sum(1440) minutes
       from     calendar
       join     overlaped_activities
       on       calendar.mdate > date(start)
       and      calendar.mdate < date(end)
       where    datediff(end, start) > 1
       group by name, mdate
    ) act
group by name, date_activity;

Endergebnis:

| name |       date_activity | min_activity |
|------|--------------------:|-------------:|
| me   | 03.04.2017 00:00:00 |      45,0000 |
| me   | 04.04.2017 00:00:00 |     105,0000 |
| me   | 05.04.2017 00:00:00 |      60,0000 |
| me   | 06.04.2017 00:00:00 |    1440,0000 |
| me   | 07.04.2017 00:00:00 |      45,0000 |  

Vergiss es fast, das Rezept: http://rextester.com/EIJOI2098

4
McNets

Wenn es nur nach Datum aufgeteilt ist und Sie DATETIME verwenden, kann es recht einfach sein. Geben Sie einfach den Zeitstempel ein und gruppieren Sie ihn nach Datum

SELECT
who,
SUBSTRING(date_created, 1, 10) AS date,
COUNT(times) AS time
FROM timings
WHERE date_created > '2017-04-03' AND date_created < '2017-04-10'
GROUP BY date
+------+------------+------+
| name |    day     | time |
+------+------------+------+
|  me  | 2017-04-03 |   45 |
|  me  | 2017-04-04 |  105 |
|  me  | 2017-04-05 |   60 |
|  me  | 2017-04-06 | 1440 |
|  me  | 2017-04-07 |   45 |
+------+------------+------+
0
user36388

In MySQL müssen wir uns mit der Tabelle mit Datumsangaben verbinden, da die Oracle-Funktion von Connect nach Ebene in MySQL nicht unterstützt wird.

Weitere Details hinzufügen ...

Create table ActivityDay 
(id INT Primary Key NOT NULL AUTO_INCREMENT, ActivityDay Date NOT NULL);

insert into ActivityDate (ActivityDay) values('2017-04-03'), 
values('2017-04-04'),values('2017-04-05'),
values('2017-04-06'),values('2017-04-07'),
values('2017-04-08');

Select id, Name, ActualStart, ActualEnd, 
(Time_to_Sec(ActualEnd) - Time_to_Sec(ActualStart))/60 time
from (
select a.id, a.name, a.start, Date(start) StartDay, Date(end) EndDay, a.time,
       case when DATE(START) = DATE(ActualStart) then Start else ActivityDay end ActualStart,
       case when Date(end) <> Date(ActualEnd) then date_add(ActivityDay, interval 24*60*60 - 1 second) else end end ActualEnd,
       ad.ActivityDay
from Activities a join ActivityDate ad) d
Where ActivityDay between start and end)
0
SQL.RK