Mit mehreren verschiedenen Fensterfunktionen und zwei Unterabfragen sollte dies recht schnell funktionieren:
WITH events(id, event, ts) AS (
VALUES
(1, 12, '2014-03-19 08:00:00'::timestamp)
,(2, 12, '2014-03-19 08:30:00')
,(3, 13, '2014-03-19 09:00:00')
,(4, 13, '2014-03-19 09:30:00')
,(5, 12, '2014-03-19 10:00:00')
)
SELECT first_value(pre_id) OVER (PARTITION BY grp ORDER BY ts) AS pre_id
, id, ts
, first_value(post_id) OVER (PARTITION BY grp ORDER BY ts DESC) AS post_id
FROM (
SELECT *, count(step) OVER w AS grp
FROM (
SELECT id, ts
, NULLIF(lag(event) OVER w, event) AS step
, lag(id) OVER w AS pre_id
, lead(id) OVER w AS post_id
FROM events
WINDOW w AS (ORDER BY ts)
) sub1
WINDOW w AS (ORDER BY ts)
) sub2
ORDER BY ts;
Verwenden von ts als Name für die Zeitstempelspalte.
Angenommen ts eindeutig sein - und indiziert (Eine eindeutige Einschränkung macht das automatisch).
In einem Test mit einer realen Tabelle mit 50.000 Zeilen war nur ein einziger Index-Scan erforderlich . Sollte also auch bei großen Tischen anständig schnell sein. Im Vergleich dazu wurde Ihre Abfrage mit join / distinct nicht nach einer Minute beendet (wie erwartet).
Sogar eine optimierte Version, die jeweils nur einen Cross-Join behandelt (der linke Join mit kaum einer einschränkenden Bedingung ist effektiv eine begrenzte cross join) wurde nach einer Minute nicht beendet.
Optimieren Sie für eine optimale Leistung mit einer großen Tabelle Ihre Speichereinstellungen, insbesondere für work_mem (für große Sortieroperationen). Erwägen Sie, es für Ihre Sitzung vorübergehend (viel) höher einzustellen, wenn Sie den Arbeitsspeicher entbehren können. Lesen Sie mehr hier und hier.
Wie?
-
In Unterabfrage
sub1Sehen Sie sich das Ereignis aus der vorherigen Zeile an und behalten Sie es nur, wenn es sich geändert hat, und markieren Sie so das erste Element einer neuen Gruppe. Holen Sie sich gleichzeitig dieidder vorherigen und der nächsten Zeile (pre_id,post_id). -
In Unterabfrage
sub2,count()zählt nur Nicht-Null-Werte. Die resultierendegrpmarkiert Peers in Blöcken aufeinanderfolgender gleicher Ereignisse. -
Im letzten
SELECT, nehmen Sie die erstepre_idund die letztepost_idpro Gruppe für jede Zeile, um zum gewünschten Ergebnis zu kommen.
Eigentlich sollte das im äußerenSELECTnoch schneller gehen :last_value(post_id) OVER (PARTITION BY grp ORDER BY ts RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS post_id... da die Sortierreihenfolge des Fensters mit dem Fenster für
pre_idübereinstimmt , sodass nur eine einzige Sortierung erforderlich ist. Ein Schnelltest scheint es zu bestätigen. Mehr über diese Rahmendefinition.
SQL-Geige.