Wie schreibe ich einen Join mit diesem ungewöhnlichen Matching-Kriterium?

8

Ich möchte "left join" einer Tabelle, damit ein Wert nicht nur mit einer übereinstimmenden Zeile verbunden wird, sondern auch mit allen nachfolgenden nicht übereinstimmenden Zeilen, bis zur nächsten übereinstimmenden Zeile. Um es anders auszudrücken, ich möchte Nullen mit dem vorherigen Nicht-Null-Wert ausfüllen.

Beispieldaten und gewünschtes Ergebnis:

Tabelle x :

%Vor%

Tabelle y :

%Vor%

Ergebnis von select x.id, y.val from x left join y on x.id=y.id order by x.id; :

%Vor%

Gewünschtes Ergebnis:

%Vor%     
jl6 07.04.2013, 10:50
quelle

7 Antworten

6

Indizes

Erstellen Sie Indizes auf x.id und y.id - die Sie wahrscheinlich bereits haben, wenn diese Ihre Primärschlüssel sind.
Ein mehrspaltiger Index kann ebenfalls hilfreich sein, insbesondere mit index scannt nur in S. 9.2 +:

%Vor%

In meinen Tests wurde dieser Index jedoch zunächst nicht verwendet. Muss (sonst sinnlos) val zu ORDER BY hinzufügen, um den Abfrageplaner davon zu überzeugen, dass die Sortierreihenfolge übereinstimmt. Siehe Abfrage 3 .

Der Index macht bei diesem synthetischen Aufbau wenig Unterschied. Bei Tabellen mit mehreren Spalten wird das Abrufen von val aus der Tabelle jedoch immer teurer, wodurch der "deckende" Index attraktiver wird.

Abfragen

1) Einfach

%Vor%

SQL Fiddle.

Weitere Erklärung für die Technik mit DISTINCT in dieser verwandten Antwort:

Ich habe einige Tests durchgeführt, weil ich den Verdacht hatte, dass die erste Abfrage nicht gut skalieren würde. Es ist schnell mit einem kleinen Tisch, aber nicht gut mit größeren Tischen. Postgres optimiert den Plan nicht und beginnt mit einem (begrenzten) Cross-Join mit Kosten von O(N²) .

2) Schnell

Diese Abfrage ist immer noch ziemlich einfach und skaliert ausgezeichnet:

%Vor%

Die Fensterfunktion lead() ist hilfreich. Ich nutze die Option, um einen Standard anzugeben, der die Ecke der letzten Zeile abdeckt: 2147483647 ist die größtmögliche ganze Zahl . Passen Sie sich Ihrem Datentyp an.

3) Sehr einfach und fast so schnell

%Vor%

Normalerweise sind korrelierte Unterabfragen eher langsam. Aber dieser kann einfach einen Wert aus dem (Deckungs-) Index auswählen und ist ansonsten so einfach, dass er mithalten kann.

Der zusätzliche ORDER BY item val (fett hervorgehoben) scheint sinnlos. Aber das Hinzufügen von es überzeugt den Abfrageplaner, dass es in Ordnung ist, den mehrspaltigen Index y_mult_idx von oben zu verwenden, da die Sortierreihenfolge übereinstimmt. Beachten Sie das

  

Nur Indexsuche mit y_mult_idx ..

in der Ausgabe EXPLAIN .

Testfall

Nach einer lebhaften Debatte und mehreren Updates sammelte ich alle bisher geposteten Fragen und machte einen Testfall für einen schnellen Überblick. Ich verwende nur 1000 Zeilen, so dass SQLfiddle nicht mit den langsameren Abfragen ausgeht. Aber die Top 4 (Erwin 2, Clodoaldo, a_horse, Erwin 3) skalieren linear in allen meinen lokalen Tests. Noch einmal aktualisiert, um meinen neuesten Zusatz zu enthalten, verbessere Format und Reihenfolge jetzt nach Leistung:

Big SQL Fiddle vergleicht die Leistung.

    
Erwin Brandstetter 07.04.2013, 12:53
quelle
4

SQL Fiddle

%Vor%     
Clodoaldo Neto 07.04.2013 11:08
quelle
4
%Vor%

SQLFiddle Beispiel: Ссылка

    
a_horse_with_no_name 07.04.2013 11:05
quelle
2
%Vor%

Bitte siehe hier .

    
fthiella 07.04.2013 11:12
quelle
2

Ich möchte die Aggregatfunktionen MIN (), MAX () oder closer_to() in Form von (NOT) EXISTS ausdrücken.

%Vor%

Mein Bauchgefühl ist, dass dies genau den gleichen Abfrageplan wie Erwins Antwort erzeugt.

    
wildplasser 07.04.2013 14:27
quelle
1

Verwenden Sie COALESCE und die Unterabfrage für die Logik.

Die Unterabfrage ruft den letzten Wert ab.

Versuchen Sie Folgendes:

%Vor%

sqlfiddle: Ссылка

    
Iswanto San 07.04.2013 11:03
quelle
0

Ich bin mir nicht sicher, wie Sie dies mit einer einzigen gespeicherten Prozedur erreichen würden. Logik ähnlich der folgenden würde Ihnen das gewünschte Ergebnis zurückgeben

%Vor%     
JogiKalpesh 07.04.2013 11:44
quelle