Ich habe eine Tabelle mit mehreren Zeitreihen verschiedener Typen. Die Zeitstempel von Kohäsionspunkten aus verschiedenen Serien stimmen nicht genau überein (dh die Differenz kann bis zu einer Stunde betragen).
Schema
Unten ist das Schema mit zwei Beispielserien:
CREATE TABLE series (id integer, series_type integer, charttime timestamp,
value integer, PRIMARY KEY (id));
INSERT INTO series VALUES (1, 1, '2018-03-01 12:10:00', 40),
(2, 1, '2018-03-01 13:25:00', 30), (3, 1, '2018-03-01 14:10:00', 50);
INSERT INTO series VALUES (4, 2, '2018-03-01 11:20:00', 2), (5, 2, '2018-03-01 12:15:00', 6),
(6, 2, '2018-03-01 13:00:00', 7), (7, 2, '2018-03-01 13:45:00', 1);
id |series_type |charttime |value |
---|------------|--------------------|------|
1 |1 |2018-03-01 12:10:00 |40 |
2 |1 |2018-03-01 13:25:00 |30 |
3 |1 |2018-03-01 14:10:00 |50 |
4 |2 |2018-03-01 11:20:00 |2 |
5 |2 |2018-03-01 12:15:00 |6 |
7 |2 |2018-03-01 13:45:00 |1 |
6 |2 |2018-03-01 13:00:00 |7 |
Tor
Ziel ist es, eine Serie zusammen mit dem nächstgelegenen Datenpunkt aus einer anderen Serie auszuwählen. Für den Beispieldatensatz sollte das Ergebnis sein:
charttime |s1 |s2 |
--------------------|---|---|
2018-03-01 12:10:00 |40 |6 |
2018-03-01 13:25:00 |30 |1 |
2018-03-01 14:10:00 |50 |1 |
Erster Arbeitsansatz
Mein aktueller Ansatz besteht darin, den am besten passenden Datenpunkt aus der anderen Reihe durch eine Unterabfrage auszuwählen:
SELECT l.charttime, l.value AS s1,
( SELECT r.value
FROM series r
WHERE ABS( EXTRACT( EPOCH FROM l.charttime - r.charttime ) / 3600 ) < 1
AND r.series_type = 2
ORDER BY ABS( EXTRACT( EPOCH FROM l.charttime - r.charttime )) ASC LIMIT 1
) AS s2
FROM series l
WHERE l.series_type = 1
ORDER BY l.charttime ASC
Dies scheint nicht der beste Ansatz zu sein, da das Dataset sehr groß ist und daher die Ausführung vieler Unterabfragen die Abfrage verlangsamt.
Zweiter Ansatz
Eine andere Idee besteht darin, die Tabelle selbst zu verknüpfen und nach Zeitstempeln für geschlossene Daten zu filtern:
SELECT l.charttime, l.value AS s1, r.charttime, r.value AS s2
FROM series l, series r
WHERE abs(EXTRACT(EPOCH FROM l.charttime - r.charttime) / 3600) < 1
AND l.series_type = 1 AND r.series_type = 2
charttime |s1 |charttime |s2 |
--------------------|---|--------------------|---|
2018-03-01 12:10:00 |40 |2018-03-01 11:20:00 |2 |
2018-03-01 12:10:00 |40 |2018-03-01 12:15:00 |6 |
2018-03-01 12:10:00 |40 |2018-03-01 13:00:00 |7 |
2018-03-01 13:25:00 |30 |2018-03-01 13:45:00 |1 |
2018-03-01 13:25:00 |30 |2018-03-01 13:00:00 |7 |
2018-03-01 14:10:00 |50 |2018-03-01 13:45:00 |1 |
Das Problem sind dann die doppelten Datenpunkte. Die Gruppierung in der ersten Spalte funktioniert nicht, da die beste Übereinstimmung s2
nicht ausgewählt werden kann.
Gibt es einen besseren Ansatz?
quelle
s2
stammen die Werte in der Spalte vonseries_type
= 2.series_type = 1
. Wie bereits erwähnt,s2
sind die Übereinstimmungspunkte vonseries_type=2
.Antworten:
In Ihrem zweiten Ansatz können Sie mit dem Self-Join Duplikate entfernen, indem Sie
row_number()
:Partition nach l.charttime, Reihenfolge nach Zeitdifferenz und Filter für row_number = 1.
Ich denke jedoch, dass die Leistung schrecklich sein wird. Aufgrund der kartesischen Verknüpfung handelt es sich um eine O-Operation (Größe (Serie 1) x Größe (Serie 2)).
Das Vorhandensein von l.charttime und r.charttime in Funktionen kann ebenfalls Probleme verursachen. Versuchen Sie das Refactoring auf (im Pseudocode)
.. und sehen, wie der Abfrageplan aussieht. Ich nehme an, es gibt einen Index für die Chartzeit. Ohne einen wird kein Ansatz schnell sein. In der Tat können zwei Teilindizes , einer für Serie 1 und einer für Serie 2, sogar noch besser sein.
quelle
Ich lese Ihre Geschäftsregel als "Holen Sie sich jede Zeile aus Serie 1 und die nächstgelegene Zeile aus Serie 2, falls vorhanden, die innerhalb von 3600 vom Wert von Serie 1 liegen muss." Ich frage mich, ob eine gute Lösung dafür kein Cursor wäre. Nun, zwei Cursor.
Der grundlegende Algorithmus besteht darin, die zwei Werte der Serie 2 zu finden, die sich über jeden Wert der Serie 1 erstrecken, dh den Wert unmittelbar davor und den Wert unmittelbar danach in zeitlicher Abfolge. Verwenden Sie dann die nächstgelegene. Es würde ungefähr so aussehen:
Ich habe offensichtlich viele Feinheiten weggelassen. Es wird einige Fälle geben, in denen mehrere Werte der Serie 1 vor dem ersten Wert der Serie 2 oder nach dem letzten Wert der Serie 2 vorliegen. Auch wenn es in Serie 2 keine Übereinstimmung für einen bestimmten Wert in Serie 1 gibt. In Ihrer Beschreibung werden die Regeln für diese nicht erwähnt, aber ich bin sicher, dass Sie sie einarbeiten können.
Dies erfordert, dass beide Serienwerte zeitlich aufeinander abgestimmt sind. Die Sorte könnte teuer sein. Es müsste jedoch einen Index für diese Spalte geben, damit eine Lösung funktioniert. Die Abfrage hätte also bereits einen zeitlich geordneten Zugriffspfad zu den Daten, und wahrscheinlich würde es zur Laufzeit keine tatsächliche Sortierung geben.
Die zeitliche Komplexität hierfür ist O (Größe (Serie 1) + Größe (Serie 2)), dh O (N) anstelle von O (N ^ 2) der Selbstkreuzverbindung.
quelle
Basierend auf der Idee von Michael Green kam ich zu folgendem Ergebnis:
Die Abfragezeit beträgt ungefähr 1s im Vergleich zu 20s in meinem ursprünglichen Ansatz.
quelle