2013-03-30 4 views
12

Diese Antwort zeigt, wie High/Low/Open/Close-Werte von einem Ticker produzieren:
Retrieve aggregates for arbitrary time intervalsPostgreSQL - wie soll ich first_value() verwenden?

Ich versuche, eine Lösung zu diesem Thema (PG 9.2) auf der Basis zu implementieren, aber Schwierigkeiten habe die in immer Korrekter Wert für first_value().

Bisher habe ich versucht, zwei Anfragen:

SELECT 
    cstamp, 
    price, 
    date_trunc('hour',cstamp) AS h, 
    floor(EXTRACT(minute FROM cstamp)/5) AS m5, 
    min(price) OVER w, 
    max(price) OVER w, 
    first_value(price) OVER w, 
    last_value(price) OVER w 
FROM trades 
Where date_trunc('hour',cstamp) = timestamp '2013-03-29 09:00:00' 
WINDOW w AS (
    PARTITION BY date_trunc('hour',cstamp), floor(extract(minute FROM cstamp)/5) 
    ORDER BY date_trunc('hour',cstamp) ASC, floor(extract(minute FROM cstamp)/5) ASC 
    ) 
ORDER BY cstamp; 

Hier ist ein Stück des Ergebnisses:

 cstamp   price  h     m5 min  max  first last 
"2013-03-29 09:19:14";77.00000;"2013-03-29 09:00:00";3;77.00000;77.00000;77.00000;77.00000 

"2013-03-29 09:26:18";77.00000;"2013-03-29 09:00:00";5;77.00000;77.80000;77.80000;77.00000 
"2013-03-29 09:29:41";77.80000;"2013-03-29 09:00:00";5;77.00000;77.80000;77.80000;77.00000 
"2013-03-29 09:29:51";77.00000;"2013-03-29 09:00:00";5;77.00000;77.80000;77.80000;77.00000 

"2013-03-29 09:30:04";77.00000;"2013-03-29 09:00:00";6;73.99004;77.80000;73.99004;73.99004 

Wie Sie sehen können, 77.8 ist nicht was ich glaube, das ist richtig Wert für first_value(), der 77.0 sein sollte.

ich aber dies könnte in der WINDOW auf die mehrdeutige ORDER BY fällig, also habe ich dies zu

ORDER BY cstamp ASC 

aber dies scheint die PARTITION auch aufregen:

 cstamp   price  h     m5 min  max  first last 
"2013-03-29 09:19:14";77.00000;"2013-03-29 09:00:00";3;77.00000;77.00000;77.00000;77.00000 

"2013-03-29 09:26:18";77.00000;"2013-03-29 09:00:00";5;77.00000;77.00000;77.00000;77.00000 
"2013-03-29 09:29:41";77.80000;"2013-03-29 09:00:00";5;77.00000;77.80000;77.00000;77.80000 
"2013-03-29 09:29:51";77.00000;"2013-03-29 09:00:00";5;77.00000;77.80000;77.00000;77.00000 

"2013-03-29 09:30:04";77.00000;"2013-03-29 09:00:00";6;77.00000;77.00000;77.00000;77.00000 

da die Werte für max und zuletzt jetzt variieren innerhalb der Partition.

Was mache ich falsch? Könnte mir jemand besser helfen, die Beziehung zwischen PARTITION und ORDER innerhalb einer WINDOW zu verstehen?


Obwohl ich eine Antwort haben, ist hier eine abgespeckte pg_dump die jedermann erlaubt, die Tabelle zu erstellen. Das einzige, was anders ist, ist der Tabellenname.

CREATE TABLE wtest (
    cstamp timestamp without time zone, 
    price numeric(10,5) 
); 

COPY wtest (cstamp, price) FROM stdin; 
2013-03-29 09:04:54 77.80000 
2013-03-29 09:04:50 76.98000 
2013-03-29 09:29:51 77.00000 
2013-03-29 09:29:41 77.80000 
2013-03-29 09:26:18 77.00000 
2013-03-29 09:19:14 77.00000 
2013-03-29 09:19:10 77.00000 
2013-03-29 09:33:50 76.00000 
2013-03-29 09:33:46 76.10000 
2013-03-29 09:33:15 77.79000 
2013-03-29 09:30:08 77.80000 
2013-03-29 09:30:04 77.00000 
\. 
+1

Es würde die Dinge vereinfachen, wenn Sie eine Testtabelle bereitstellen, zum Beispiel als [SQLfiddle (zufälliges Beispiel)] (http://sqlfiddle.com/#!12/2346e). –

+0

Sicher die erste Reihenfolge, indem Sie alle Zeilen innerhalb der Partition gleich machen. So kann jede der Reihen zur Faust oder zum Letzten werden. Jetzt ist die zweite die richtige. Es sieht für mich aus, dass es funktionieren sollte. In Ihrem Bericht fehlt etwas. Wie @Erwin Ihnen sagte, produzieren Sie Testdaten, wo diese reproduziert werden können. –

+0

@ErwinBrandstetter Es tut mir leid wegen der fehlenden Testtabelle. Ich habe eine am Ende der Frage hinzugefügt. Ich fürchte, ich fand SQLFiddle ein bisschen einschüchternd - ich bekomme meine Füße ziemlich oft nicht so nass. –

Antwort

16

SQL Fiddle

Alle Funktionen Sie wirken auf den Fensterrahmen, nicht auf der Partition. Wenn nicht angegeben, ist das Rahmenende die aktuelle Zeile. Um den Fensterrahmen zu machen zu sein die ganze Partition, um sie in der Rahmen-Klausel deklariert (range...):

SELECT 
    cstamp, 
    price, 
    date_trunc('hour',cstamp) AS h, 
    floor(EXTRACT(minute FROM cstamp)/5) AS m5, 
    min(price) OVER w, 
    max(price) OVER w, 
    first_value(price) OVER w, 
    last_value(price) OVER w 
FROM trades 
Where date_trunc('hour',cstamp) = timestamp '2013-03-29 09:00:00' 
WINDOW w AS (
    PARTITION BY date_trunc('hour',cstamp) , floor(extract(minute FROM cstamp)/5) 
    ORDER BY cstamp 
    range between unbounded preceding and unbounded following 
    ) 
ORDER BY cstamp; 
+0

Vielen Dank. Als du antwortest, bin ich auf die Rahmenklausel gestoßen und sie repariert tatsächlich Dinge. Was für ein Tag! –

3

Das Ergebnis max() als Fensterfunktion auf der Basis Definition Rahmen ist.

Der Standardrahmen Definition (mit ORDER BY) von dem Start des Rahmens bis zum letzten peer der aktuellen Zeile (einschließlich der aktuellen Zeile und möglicherweise mehr Reihen Ranking gleich nach ORDER BY). In Abwesenheit von ORDER BY (wie in meiner Antwort Sie beziehen sich auf) oder ORDER BY behandelt jede Zeile in der Partition als gleich (wie in Ihrem ersten Beispiel), alle Zeilen in der Partition sind Peers, und max() erzeugt das gleiche Ergebnis für Jede Zeile in der Partition berücksichtigt effektiv alle Zeilen der Partition.

Per documentation:

Die Framing-Option Standard ist RANGE UNBOUNDED PRECEDING, die die gleiche wie RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ist. Mit ORDER BY, Dies legt den Rahmen auf alle Zeilen von der Partition Start durch den letzten Peer der aktuellen Zeile.Ohne ORDER BY sind alle Zeilen der Partition im Fensterrahmen enthalten, da alle Zeilen zu Peers der aktuellen Zeile werden.

Fett Hervorhebung meins.

Die einfache Lösung wäre die ORDER BY in der Fensterdefinition weglassen - genau wie ich in dem Beispiel, auf das Sie sich beziehen, gezeigt habe.

Alle wichtigen Details zu den Rahmenspezifikationen finden Sie im Kapitel Window Function Calls im Handbuch.

+0

Ohne einen Auftrag von, wie man ein erstes und letztes mit einer echten Bedeutung hat? –

+0

@ClodoaldoNeto: Das sind dann willkürliche Picks. Je nachdem, was das OP erreichen möchte, wäre eine Rahmendefinition wie in Ihrer Antwort oder eine separate Fensterdefinition oder nur willkürliche Auswahl (kein "ORDER BY" wie ich vorschlage) der richtige Weg. –

+0

@ErwinBrandstetter Ja, ich wollte wirklich zuerst und zuletzt * in der Zeit * gemäß der Cstamp-Spalte. Danke für Ihre Hilfe. –

8

Hier ist eine schnelle Abfrage, das Verhalten zu veranschaulichen:

select 
    v, 
    first_value(v) over w1 f1, 
    first_value(v) over w2 f2, 
    first_value(v) over w3 f3, 
    last_value (v) over w1 l1, 
    last_value (v) over w2 l2, 
    last_value (v) over w3 l3, 
    max  (v) over w1 m1, 
    max  (v) over w2 m2, 
    max  (v) over w3 m3, 
    max  (v) over() m4 
from (values(1),(2),(3),(4)) t(v) 
window 
    w1 as (order by v), 
    w2 as (order by v rows between unbounded preceding and current row), 
    w3 as (order by v rows between unbounded preceding and unbounded following) 

Der Ausgang der obigen Abfrage kann gesehen hier (SQLFiddle here) werden:

| V | F1 | F2 | F3 | L1 | L2 | L3 | M1 | M2 | M3 | M4 | 
|---|----|----|----|----|----|----|----|----|----|----| 
| 1 | 1 | 1 | 1 | 1 | 1 | 4 | 1 | 1 | 4 | 4 | 
| 2 | 1 | 1 | 1 | 2 | 2 | 4 | 2 | 2 | 4 | 4 | 
| 3 | 1 | 1 | 1 | 3 | 3 | 4 | 3 | 3 | 4 | 4 | 
| 4 | 1 | 1 | 1 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 

Nur wenige Menschen der impliziten Rahmen denken, die Fensterfunktionen angewendet werden, die einenehmenKlausel. In diesem Fall sind Fenster standardmäßig auf den Frame ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW eingestellt. Denken Sie an es auf diese Weise:

  • Auf der Zeile mit v = 1 der Rahmen des bestellten Fenster v IN (1) mit v = 2
  • Auf der Reihe erstreckt sich über den Rahmen des bestellten Fenster v IN (1, 2) mit v = 3
  • Auf der Reihe erstreckt sich über den Rahmen des bestellten Fenster Spannweiten v IN (1, 2, 3)
  • auf die Zeile mit dem Rahmen des v = 4 geordneten Fensters umspannt v IN (1, 2, 3, 4)

Wenn Sie dieses Verhalten verhindern wollen, haben Sie zwei Möglichkeiten:

  • Verwenden Sie eine explizite ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING Klausel für bestellt Fensterfunktionen
  • keine ORDER BY Klausel in diesen Fensterfunktionen verwenden, die für das Weglassen ihnen ermöglichen (als MAX(v) OVER())

Weitere Details sind in this article about LEAD(), LAG(), FIRST_VALUE() and LAST_VALUE()

erklärt
+0

Sehr schöne Demo! Vielleicht benennen Sie den hinzugefügten 'max (v) über()' 'm4', um die Symmetrie beizubehalten? –

+0

@ErwinBrandstetter: Eh ... ;-) Naja, fair genug –

+1

Wir würden nicht wollen, dass ... uhm .. irgendjemandes OCD agiert, würden wir jetzt? :) –