PDA

Vollständige Version anzeigen : Zeiträume addieren


tcg9000
21.02.2008, 14:20
Hallo,
ich habe folgendes Problem und komme da nicht weiter. Kann mir da jemand weiterhelfen? Wäre sehr nett.

In meiner Tabelle steht z.B. folgendes:

ID Start Stopp
1 01.01.2008 11:15:00 01.01.2008 11:19:00
1 02.01.2008 10:15:00 02.01.2008 10:20:00
1 02.01.2008 10:19:00 02.01.2008 12:19:00
2 15.01.2008 20:22:22 16.01.2008 20:30:35
3 21.01.2008 11:15:00 21.01.2008 11:19:00
3 22.01.2008 10:15:00 24.01.2008 10:19:00

Die Differenzen sollen berechnet werden, aber wenn es sich bei einer ID wie bei der ID 1 überlappt, muß das natürlich rausgerechnet werden.

also:

ID Start Stopp
1 01.01.2008 11:15:00 01.01.2008 11:19:00 = 4 Minuten
1 02.01.2008 10:15:00 02.01.2008 10:20:00 = 5 Minuten
1 02.01.2008 10:19:00 02.01.2008 12:19:00 = 120 Minuten

Die erste Zeile stimmt noch.
Da sich der Zeitraum von der zweiten Zeile und der von der 3. Zeile überschneidet stimmt das natürlich nicht. Die dritte Zeile müßte dann also Start 10:20 Uhr haben und nicht 10:19 Uhr und das Ergebnis müßte dann 119 Minuten sein.

Das Gesamtergebnis der ID1 wäre richtigerweise 128 Minuten.

Es können alle mögliche Arten von Zeitüberschneidungen vorkommen, es könnte auch sein, dass eine Zeile ganz rausfällt weil ein anderer Zeitraum diesen schon umschließt:

ID Start Stopp
4 01.01.2008 11:15:00 01.01.2008 11:19:00 = 4 Minuten
4 02.01.2008 10:15:00 02.01.2008 10:20:00 = 5 Minuten
4 02.01.2008 10:15:00 02.01.2008 12:19:00 = 120 Minuten

in dem Fall müßte dann die zweite Zeile rausfliegen.

Gesamtergebnis: 124 Minuten

Es kann unterschiedlich viele gleiche IDs geben, z.B.:

ID 1 : 3 Zeilen
ID 2 : 1 Zeile
ID 3 : 2 Zeilen
ID 4 : 3 Zeilen
ID 5 : 5 Zeilen
ID 6 : 7 Zeilen
ID 7 : 10 Zeilen

Hat da jemand eine Idee?

Namina
21.02.2008, 17:33
Vorausgesetzt, dass Werte mit der selben ID immer in zeitlich aufeinanderfolgender Reihenfolge untereinander stehen
(jüngstes oben, ältestes unten, und die IDs immer nacheinander, ohne dass z.B. zwischen 1 1 die 2 steht) und die nachfolgende Zeile immer ein höhere oder gleiche Stopzeit wie die Zeile vorher hat:

Wenn ID = ID_Zeile_vorher dann
wenn start > stop zeile vorher dann
dauer = stop - start
sonst
dauer = stop - stop_Zeile_vorher

tcg9000
21.02.2008, 18:07
Danke für die Antwort.

Aber so funktioniert es leider nicht. Alle Zeilen müssen miteinander verglichen werden, da es vorkommen kann, dass sich die Daten mehrfach überschneiden. Anders gesagt es kann sein, dass Zeitraum 1 mit Zeitraum 2 und Zeitraum 3 sich überschneidet... Es muß viel flexibler behandelt werden. Ich denke man müßte es irgendwie in eine Funktion reinpacken und jedes Datum einer ID mit jedem Datum vergleichen, weil alle möglichen Varianten vorkommen können. Ich weiß nur nicht wie...

ebs17
21.02.2008, 22:20
Ungenauigkeit in der Beschreibung:
ID Start Stopp
4 01.01.2008 11:15:00 01.01.2008 11:19:00 = 4 Minuten
4 02.01.2008 10:15:00 02.01.2008 10:20:00 = 5 Minuten
4 02.01.2008 10:15:00 02.01.2008 12:19:00 = 120 Minuten <- eher 124

Anbei ein Lösungsansatz (funktioniert mit den beschriebenen Regeln und dem dargestellten Datenbestand):
Public Function SummeZeiten(vID As Long) As String
Dim rs As DAO.Recordset
Dim vArray As Variant
Dim anzZ As Integer
Dim dZwiSu As Double
Dim i As Integer

'Datensätze in Array überführen
Set rs = CurrentDb.OpenRecordset("SELECT T.Start, T.Stopp " _
& "FROM tblZeitdifferenzen AS T WHERE T.Id = " & vID, dbOpenSnapshot)
rs.MoveLast
rs.MoveFirst
vArray = rs.GetRows(rs.RecordCount) 'überführt das gesamte Recordset in ein Variant-Array
rs.Close
Set rs = Nothing
'Anzahl Datensätze
anzZ = UBound(vArray, 2)

'Array-Daten nach Regeln korrigieren
'vArray(intFeld, intZeile)
'1 - Zeitraum wird durch Folgezeitraum umschlossen
If anzZ > 0 Then
For i = 0 To anzZ - 1
If vArray(0, i) >= vArray(0, i + 1) And vArray(1, i) <= vArray(1, i + 1) Then
'Stopp=Start -> Differenz=0
vArray(1, i) = vArray(0, i)
End If
Next i
End If
'2 - Stopp größer Start des Folgezeitraums
If anzZ > 0 Then
For i = 0 To anzZ - 1
If vArray(1, i) > vArray(0, i + 1) Then
'Korrektur Start
vArray(0, i + 1) = vArray(1, i)
End If
Next i
End If

'Ausgabe
dZwiSu = 0
For i = 0 To anzZ
'Debug.Print Format((vArray(1, i) - vArray(0, i)), "hh:nn:ss") 'Einzeldifferenzen
dZwiSu = dZwiSu + (vArray(1, i) - vArray(0, i))
Next i

SummeZeiten = Format(dZwiSu, "hh:nn:ss")
End Function

Debug.Print SummeZeiten(1) -> 02:08:00
Debug.Print SummeZeiten(2) -> 00:08:13
Debug.Print SummeZeiten(3) -> 00:08:00
Debug.Print SummeZeiten(4) -> 02:08:00

Josef P.
22.02.2008, 09:50
ID Start Stopp
1 01.01.2008 11:15:00 01.01.2008 11:19:00 = 4 Minuten
1 02.01.2008 10:15:00 02.01.2008 10:20:00 = 5 Minuten
1 02.01.2008 10:19:00 02.01.2008 12:19:00 = 120 Minuten

Die erste Zeile stimmt noch.
Da sich der Zeitraum von der zweiten Zeile und der von der 3. Zeile überschneidet stimmt das natürlich nicht. Die dritte Zeile müßte dann also Start 10:20 Uhr haben und nicht 10:19 Uhr und das Ergebnis müßte dann 119 Minuten sein.
Und nun soll der Datenmist per Anwendung ausgebügelt werden?
Warum stehen überhaupt Überschneidungen in den Datensätzen, wenn es keine geben darf?
=> Willst du die Werte korrigieren, oder sollen sie bestehen bleiben und jedesmal per Code korrigiert werden?

Eine SQL-Anweisung zur Auswertung könnte möglicherweise so aussehen:
SELECT X.ID, X.Start, X.Stopp, DateDiff("n",[X].[Start],[X].[Stopp]) AS Diff
FROM
(
SELECT T1.ID, Min(T1.Start) AS Start, Max(T1.Stopp) AS Stopp
FROM tabZeitRaume AS T1 INNER JOIN tabZeitRaume AS T2
ON (
T1.ID = T2.ID
AND T1.Start < [T2].[Stopp] AND T1.Stopp > [T2].[Start]
AND T1.Start <> T2.Start AND T1.Stopp <> T2.Stopp
)
GROUP BY T1.ID
UNION ALL
SELECT T1.ID, T1.Start AS Start, T1.Stopp AS Stopp
FROM tabZeitRaume AS T1 LEFT JOIN tabZeitRaume AS T2
ON (
T1.ID = T2.ID
AND T1.Start < [T2].[Stopp] AND T1.Stopp > [T2].[Start]
AND T1.Start <> T2.Start AND T1.Stopp <> T2.Stopp
)
where T2.ID is null
) X

Anm.: statt T1.Start <> T2.Start AND T1.Stopp <> T2.Stopp wäre eine eindeute Kennung je DS besser (sicherer) zum Auswerten. .. dieser Ausdruck stellt sicher, dass sich eine DS nicht auf sich selbst beziehen kann.

tcg9000
22.02.2008, 12:59
Danke erst mal für die Beiträge :-)

Aber irgendwie funktioniert es nicht richtig bzw. meine Beispiele waren wohl doch nicht so gut wie ich dachte, deshalb habe ich jetzt nochmal andere Beispiele erzeugt:

id Start Stopp
1 28.01.2008 11:44:01 29.01.2008 01:00:00
2 10.12.2007 12:00:20 10.12.2007 15:57:07
2 11.12.2007 15:02:16 14.01.2008 13:49:30
2 17.12.2007 07:26:42 31.12.2007 14:17:37
2 20.01.2008 07:54:19 28.01.2008 08:29:44
2 26.01.2008 06:33:08 28.01.2008 08:30:23
3 11.12.2007 12:03:00 11.12.2007 15:00:21
3 04.01.2008 14:39:21 23.01.2008 05:40:00
4 20.12.2007 12:01:29 10.01.2008 11:59:04
4 12.12.2007 13:05:37 20.12.2007 07:06:40
4 20.12.2007 07:08:18 20.12.2007 11:54:41
5 08.01.2008 00:08:08 08.01.2008 13:46:53
5 02.01.2008 16:18:34 08.01.2008 00:07:58
5 02.01.2008 12:33:10 03.01.2008 13:10:36
5 14.12.2007 18:17:31 19.12.2007 16:56:03
5 19.12.2007 11:51:39 08.01.2008 00:07:37
6 03.01.2008 03:24:56 04.01.2008 13:56:19
6 04.01.2008 13:51:35 04.01.2008 13:51:58
6 04.01.2008 13:52:33 04.01.2008 13:56:00
7 19.12.2007 16:56:08 07.01.2008 11:30:07
7 18.12.2007 07:55:47 07.01.2008 11:30:18
7 18.12.2007 11:11:04 07.01.2008 11:30:27
8 20.12.2007 21:52:47 29.12.2007 14:17:02
9 18.12.2007 12:36:04 02.01.2008 09:16:53

Als Ergebnis muß eine Summe herauskommen, die nur die Zeiten aufsummiert, ohne die Überschneidungen.

Die Überschneidungen kommen dadurch zustande, dass Teilprozesse gestoppt werden die Bestandteil eines Gesamtprozesses sind. Nun soll die Summe ermittelt werden wie lange der Gesamtprozess gestoppt wurde. Ich kann´s leider nicht besser beschreiben und hoffe weiterhin dass mir jemande helfen kann. Danke!

Josef P.
22.02.2008, 13:03
Das Ergebnis meiner SQL-Anweisung hast du dir angesehen?
... Die Gruppierung über die ID und Summierung der Minuten fehlt noch, aber ansonsten sollte sie - so hoffe ich zumindest - das richtige Ergebnis bringen.

Testen werde ich es aber nicht, da ich keine Lust habe, den von dir geposteten Text manuell in eine Tabelle zu übertragen. ;)

ebs17
22.02.2008, 13:27
keine Lust habe, den von dir geposteten Text manuell in eine Tabelle zu übertragen
Dem kann ich mich nur anschließen.

tcg9000
23.02.2008, 08:11
Habe die Testdaten, als Exceldatei hochgeladen...

Ja, ich habe beide Vorschläge angesehen und es funktioniert leider net ganz so. Das wird klar, hoffe ich zumindest, wenn man die neuen Testdaten verwendet.
So langsam glaube ich, dass es wohl doch keine Lösung für mein Problem gibt...

Josef P.
23.02.2008, 08:17
Das kommt bei mir als Ergebnis:
ID Minuten
1 796
2 68965
3 26998
4 41685
5 35728
6 2076
7 84868
8 12505
9 21400

/edit: Werte passen aber nicht, da die Gruppierung bei mehrfachen Überschneidungen pro ID Lücken übersieht.

=>
SELECT X.ID, X.Start, X.Stopp, Datediff("n", x.Start, X.Stopp) AS Minuten
FROM
(
SELECT G2.ID, Min(G2.Start) AS Start, Max(G2.Stopp) AS Stopp
FROM
(
SELECT G1.ID, Min(G1.Start) AS Start, Max(G1.Stopp) AS Stopp
FROM
(
SELECT T1.ID, Min(T1.Start) AS Start, Max(T1.Stopp) AS Stopp
FROM tabZeitRaume AS T1 INNER JOIN tabZeitRaume AS T2
ON (T1.ID = T2.ID AND T1.Start < [T2].[Stopp] AND T1.Stopp > [T2].[Start] AND NOT (T1.Start = T2.Start AND T1.Stopp = T2.Stopp) )
GROUP BY
T1.ID,
iif(T1.Start between T2.Start and T2.Stopp, T2.Start, T1.Start),
iif(T1.Stopp between T2.Start and T2.Stopp, T2.Stopp, T1.Stopp)
) G1
GROUP BY G1.ID, G1.Stopp
) G2
GROUP BY G2.ID, G2.Start

UNION ALL

SELECT T1.ID, T1.Start AS Start, T1.Stopp AS Stopp
FROM tabZeitRaume AS T1 LEFT JOIN tabZeitRaume AS T2
ON (T1.ID = T2.ID AND T1.Start < [T2].[Stopp] AND T1.Stopp > [T2].[Start] AND NOT (T1.Start = T2.Start AND T1.Stopp = T2.Stopp))
where T2.ID is null
) AS X
Order By X.ID, X.Start, X.Stopp


BTW: Nächstes mal aber bitte die Beispieldaten gleich in eine mdb inkl. deiner fehlgeschlagenen Versuche.

tcg9000
23.02.2008, 10:47
Danke erst mal für die schnelle Antwort.
Auf den ersten Blick sieht es sehr gut aus. Ich muß das erst mal genau nachprüfen, kann ne Weile dauern...
Ok, deinen Vorschlag werde ich beim nächsten Mal beherzigen.
Nochmals vielen Dank und ich poste nochmal ob jetzt alle Fälle abgedeckt sind.

tcg9000
25.02.2008, 16:33
Hallo Josef P,

so, jetzt habe ich das ganze mal genau überprüft und es ist z.B. bei ID 5 so, wie du es erwähnst, dass er bei Mehrfachüberschneidungen das trotzdem net mitkriegt, jetzt wollte ich einfach nochmal eine gleiche zweite Abfrage dahintersetzen, damit das ausgebügelt wird - mir fällt nix besseres ein. Sobald ich aber in dem SQL String die Source "tabZeitRaume" in einen anderen Namen abändere z.B. in "tabZeitRaum" kommt ein Syntax Error "Syntaxfehler in From-Klausel"
Was mache ich denn jetzt schon wieder falsch?

Josef P.
25.02.2008, 17:32
wie du es erwähnst, dass er bei Mehrfachüberschneidungen das trotzdem net mitkriegt,
... und ich dachte, das wäre mit der Anweisung aus #10 ausgebügelt. :bawling:

Syntax Error "Syntaxfehler in From-Klausel"
An diese Meldung muss man sich gewöhnen, wenn man etwas umfangreicherer SQL-Anweisungen für Jet erstellt. ;)

Such einmal in der Abfrage, ob ein select statt mit runden klammern mit eckigen umfasst ist.
statt:
.. from (SELECT ... ) AS X
steht dann so etwas:
... from [select ... ]. AS X
...wenn das der Fall ist, dann hat der Abfrage-Optimierer von Access zugeschlagen.

tcg9000
25.02.2008, 19:03
ja, genau, da waren die falsche Klammern drin, genau wie von Dir beschrieben. Danke! Jetzt habe ich die zwei Abfragen und dann paßt auch das Ergebnis. Jetzt ist es so wie es sein soll.
Vielen Dank nochmal für Deine Mithilfe. Ohne wäre ich völlig aufgeschmissen gewesen.

Josef P.
25.02.2008, 19:11
Wenn ich die SQL-Anweisung aus #10 gruppiere bekomme ich dieses Ergebnis:
ID Summe
1 796
2 60680
3 26998
4 41685
5 35728
6 2072
7 29015
8 12505
9 21400
Was kommt bei dir heraus?
Wie sieht deine Abfrage nun aus?