demo:db<>fiddle (verwendet den alten Datensatz mit dem überlappenden A-B-Teil)
Haftungsausschluss: Dies funktioniert für Tagesintervalle, nicht für Zeitstempel. Die Anforderung für ts kam später.
SELECT
s.acts,
s.sum,
MIN(a.start) as start,
MAX(a.end) as end
FROM (
SELECT DISTINCT ON (acts)
array_agg(name) as acts,
SUM(count)
FROM
activities, generate_series(start, "end", interval '1 day') gs
GROUP BY gs
HAVING cardinality(array_agg(name)) > 1
) s
JOIN activities a
ON a.name = ANY(s.acts)
GROUP BY s.acts, s.sum
generate_series
generiert alle Daten zwischen Start und Ende. Jedes Datum, an dem eine Aktivität existiert, erhält also eine Zeile mit der spezifischencount
- Gruppieren aller Daten, Aggregieren aller vorhandenen Aktivitäten und Summe ihrer Anzahl
HAVING
filtert die Daten heraus, an denen nur eine Aktivität vorhanden ist- Weil es verschiedene Tage mit denselben Aktivitäten gibt, brauchen wir nur einen Vertreter:Filtern Sie alle Duplikate mit
DISTINCT ON
- Fügen Sie dieses Ergebnis mit der ursprünglichen Tabelle zusammen, um den Anfang und das Ende zu erhalten. (Beachten Sie, dass "Ende" ein reserviertes Wort in Postgres ist, Sie sollten besser einen anderen Spaltennamen finden!). Früher war es bequemer, sie zu verlieren, aber es ist möglich, diese Daten innerhalb der Unterabfrage zu erhalten.
- Gruppieren Sie diesen Join, um das früheste und späteste Datum jedes Intervalls zu erhalten.
Hier ist eine Version für Zeitstempel:
WITH timeslots AS (
SELECT * FROM (
SELECT
tsrange(timepoint, lead(timepoint) OVER (ORDER BY timepoint)),
lead(timepoint) OVER (ORDER BY timepoint) -- 2
FROM (
SELECT
unnest(ARRAY[start, "end"]) as timepoint -- 1
FROM
activities
ORDER BY timepoint
) s
)s WHERE lead IS NOT NULL -- 3
)
SELECT
GREATEST(MAX(start), lower(tsrange)), -- 6
LEAST(MIN("end"), upper(tsrange)),
array_agg(name), -- 5
sum(count)
FROM
timeslots t
JOIN activities a
ON t.tsrange && tsrange(a.start, a.end) -- 4
GROUP BY tsrange
HAVING cardinality(array_agg(name)) > 1
Die Hauptidee besteht darin, mögliche Zeitfenster zu identifizieren. Also nehme ich jede bekannte Zeit (sowohl Start als auch Ende) und füge sie in eine sortierte Liste ein. So kann ich die ersten bekannten Schleppzeiten (17:00 von Start A und 18:00 von Start B) nehmen und prüfen, welches Intervall drin ist. Dann überprüfe ich es für den 2. und 3., dann für den 3. und 4. und so weiter.
In den ersten Zeitschlitz passt nur A. Im zweiten von 18-19 passt auch B. Im nächsten Slot 19-20 auch C, von 20 bis 20:30 passt A nicht mehr, nur noch B und C. Der nächste ist 20:30-22 wo nur B passt, schließlich kommt 22-23 D dazu B und nicht zuletzt nur D passt in 23-23:30.
Also nehme ich diese Zeitliste und verbinde sie mit der Aktivitätstabelle, wo sich die Intervalle schneiden. Danach ist es nur eine Gruppierung nach Zeitfenster und die Zusammenfassung Ihrer Zählung.
- dies fügt beide ts einer Zeile in ein Array ein, dessen Elemente mit
unnest
zu einer Zeile pro Element expandiert werden . So bekomme ich alle mal in eine Spalte die einfach bestellt werden kann - unter Verwendung der führenden Fensterfunktion
ermöglicht es, den Wert der nächsten Zeile in die aktuelle zu übernehmen. Also kann ich aus diesen beiden Werten mit
tsrange
einen Zeitstempelbereich erstellen - Dieser Filter ist notwendig, da die letzte Zeile keinen "nächsten Wert" hat. Dadurch wird ein
NULL
erstellt Wert, der vontsrange
interpretiert wird als unendlich. Das würde also ein unglaublich falsches Zeitfenster schaffen. Also müssen wir diese Zeile herausfiltern. - Nehmen Sie an den Zeitfenstern gegen den ursprünglichen Tisch teil. Der
&&
Operator prüft, ob sich zwei Bereichstypen überschneiden. - Gruppierung nach einzelnen Zeitfenstern, Aggregation der Namen und der Anzahl. Filtern Sie die Zeitfenster mit nur einer Aktivität heraus, indem Sie den
HAVING
verwenden Klausel - Ein bisschen knifflig, um die richtigen Start- und Endpunkte zu finden. Die Startpunkte sind also entweder das Maximum des Aktivitätsstarts oder der Beginn eines Zeitfensters (das mit
lower
abgerufen werden kann ). Z.B. Nehmen Sie den 20-20:30-Slot:Er beginnt um 20 Uhr, aber weder B noch C haben dort ihren Startpunkt. Ähnlich der Endzeit.