AsOf-Join
Was ist ein AsOf-Join?
Zeitreihendaten sind nicht immer perfekt ausgerichtet. Uhren können leicht abweichend gehen, oder es kann eine Verzögerung zwischen Ursache und Wirkung geben. Das erschwert die Verknüpfung zweier geordneter Datensätze. AsOf-Joins sind ein Werkzeug, um dieses und ähnliche Probleme zu lösen.
Ein Problem, das AsOf-Joins lösen, ist das Ermitteln des Werts einer sich ändernden Eigenschaft zu einem bestimmten Zeitpunkt. Dieser Anwendungsfall ist so häufig, dass der Name daher stammt:
Gib mir den Wert der Eigenschaft zum Stand dieses Zeitpunkts (as of this time).
Allgemeiner verkörpern AsOf-Joins jedoch gängige Semantiken der zeitlichen Analyse, die in Standard-SQL umständlich und langsam umzusetzen sind.
Beispiel-Datensatz Portfolio
Beginnen wir mit einem konkreten Beispiel.
Angenommen, wir haben eine Tabelle mit Aktien-prices und Zeitstempeln:
| ticker | when | price |
|---|---|---|
| APPL | 2001-01-01 00:00:00 | 1 |
| APPL | 2001-01-01 00:01:00 | 2 |
| APPL | 2001-01-01 00:02:00 | 3 |
| MSFT | 2001-01-01 00:00:00 | 1 |
| MSFT | 2001-01-01 00:01:00 | 2 |
| MSFT | 2001-01-01 00:02:00 | 3 |
| GOOG | 2001-01-01 00:00:00 | 1 |
| GOOG | 2001-01-01 00:01:00 | 2 |
| GOOG | 2001-01-01 00:02:00 | 3 |
Wir haben eine weitere Tabelle mit Portfolio-holdings zu verschiedenen Zeitpunkten:
| ticker | when | shares |
|---|---|---|
| APPL | 2000-12-31 23:59:30 | 5.16 |
| APPL | 2001-01-01 00:00:30 | 2.94 |
| APPL | 2001-01-01 00:01:30 | 24.13 |
| GOOG | 2000-12-31 23:59:30 | 9.33 |
| GOOG | 2001-01-01 00:00:30 | 23.45 |
| GOOG | 2001-01-01 00:01:30 | 10.58 |
| DATA | 2000-12-31 23:59:30 | 6.65 |
| DATA | 2001-01-01 00:00:30 | 17.95 |
| DATA | 2001-01-01 00:01:30 | 18.37 |
Um diese Tabellen in DuckDB zu laden, führen Sie aus:
CREATE TABLE prices AS FROM 'https://duckdb.org/data/prices.csv';CREATE TABLE holdings AS FROM 'https://duckdb.org/data/holdings.csv';Innere AsOf-Joins
Wir können den Wert jeder Position zu diesem Zeitpunkt berechnen, indem wir den jüngsten Preis vor dem Zeitstempel der Position mit einem AsOf-Join finden:
SELECT h.ticker, h.when, price * shares AS valueFROM holdings hASOF JOIN prices p ON h.ticker = p.ticker AND h.when >= p.when;Dadurch wird jeder Zeile der Wert der Position zu diesem Zeitpunkt zugeordnet:
| ticker | when | value |
|---|---|---|
| APPL | 2001-01-01 00:00:30 | 2.94 |
| APPL | 2001-01-01 00:01:30 | 48.26 |
| GOOG | 2001-01-01 00:00:30 | 23.45 |
| GOOG | 2001-01-01 00:01:30 | 21.16 |
Im Wesentlichen wird eine Funktion ausgeführt, die nahegelegene Werte in der Tabelle prices nachschlägt.
Beachten Sie außerdem, dass fehlende ticker-Werte keine Übereinstimmung haben und nicht in der Ausgabe erscheinen.
Äußere AsOf-Joins
Weil AsOf höchstens eine Übereinstimmung von der rechten Seite liefert, wächst die linke Tabelle durch den Join nicht, sie kann aber schrumpfen, wenn auf der rechten Seite Zeitpunkte fehlen. Für diese Situation können Sie einen äußeren AsOf-Join verwenden:
SELECT h.ticker, h.when, price * shares AS valueFROM holdings hASOF LEFT JOIN prices p ON h.ticker = p.ticker AND h.when >= p.whenORDER BY ALL;Wie zu erwarten, entstehen dann NULL-Preise und -Werte, statt Zeilen der linken Seite zu verwerfen,
wenn kein Ticker vorhanden ist oder der Zeitpunkt vor dem Beginn der Preise liegt.
| ticker | when | value |
|---|---|---|
| APPL | 2000-12-31 23:59:30 | |
| APPL | 2001-01-01 00:00:30 | 2.94 |
| APPL | 2001-01-01 00:01:30 | 48.26 |
| GOOG | 2000-12-31 23:59:30 | |
| GOOG | 2001-01-01 00:00:30 | 23.45 |
| GOOG | 2001-01-01 00:01:30 | 21.16 |
| DATA | 2000-12-31 23:59:30 | |
| DATA | 2001-01-01 00:00:30 | |
| DATA | 2001-01-01 00:01:30 |
AsOf-Joins mit dem Schlüsselwort USING
Bisher haben wir die Bedingungen für AsOf explizit angegeben,
SQL hat aber auch eine vereinfachte Join-Bedingungssyntax
für den häufigen Fall, dass die Spaltennamen in beiden Tabellen gleich sind.
Diese Syntax verwendet das Schlüsselwort USING, um die Felder aufzulisten, die auf Gleichheit verglichen werden sollen.
AsOf unterstützt diese Syntax ebenfalls, allerdings mit zwei Einschränkungen:
- Das letzte Feld ist die Ungleichung
- Die Ungleichung ist
>=(der häufigste Fall)
Unsere erste Abfrage kann dann so geschrieben werden:
SELECT ticker, h.when, price * shares AS valueFROM holdings hASOF JOIN prices p USING (ticker, "when");Klarstellung zur Spaltenauswahl mit USING in ASOF-Joins
Wenn Sie das Schlüsselwort USING in einem Join verwenden, werden die in der USING-Klausel angegebenen Spalten in der Ergebnismenge zusammengeführt. Das bedeutet: Wenn Sie Folgendes ausführen:
SELECT *FROM holdings hASOF JOIN prices p USING (ticker, "when");Sie erhalten nur die Spalten h.ticker, h.when, h.shares, p.price. Die Spalten ticker und when erscheinen nur einmal, wobei ticker
und when aus der linken Tabelle (holdings) stammen.
Dieses Verhalten ist für die Spalte ticker in Ordnung, weil der Wert in beiden Tabellen gleich ist. Für die Spalte when können die Werte
zwischen den beiden Tabellen jedoch aufgrund der Bedingung >= im AsOf-Join abweichen. Der AsOf-Join ist so ausgelegt, dass jede Zeile der linken
Tabelle (holdings) anhand der Spalte when mit der nächstgelegenen vorhergehenden Zeile der rechten Tabelle (prices) abgeglichen wird.
Wenn Sie die Spalte when aus beiden Tabellen abrufen möchten, um beide Zeitstempel zu sehen, müssen Sie die Spalten explizit auflisten statt
sich auf * zu verlassen, etwa so:
SELECT h.ticker, h.when AS holdings_when, p.when AS prices_when, h.shares, p.priceFROM holdings hASOF JOIN prices p USING (ticker, "when");So erhalten Sie die vollständigen Informationen aus beiden Tabellen und vermeiden mögliche Verwirrung durch das Standardverhalten des
Schlüsselworts USING.
Siehe auch
Implementierungsdetails finden Sie im Blogbeitrag „DuckDB’s AsOf joins: Fuzzy Temporal Lookups“.