2015-10-16 12 views
5

PostgreSQL 9.4 Die Tabelle wird wie folgt erstellt:Warum kann Index Only Scan nicht für Indizes verwendet werden, die mit COALESCE erstellt wurden?

CREATE TABLE foo (
    id integer, 
    date date, 
    value numeric(14,3) 
); 

ich eine Abfrage mit der ROW_NUMBER() Fensterfunktion und COALESCE zu optimieren. Für die effizienteste, neige ich Index Only Scan in der folgenden Abfrage verwenden:

SELECT id, c_val 
FROM (
    SELECT id, COALESCE(value, 0) c_val, ROW_NUMBER() OVER(PARTITION BY id ORDER BY date DESC NULLS LAST) rn 
    FROM foo) sbt 
WHERE sbt.rn = 1; 

Also, wenn ich den Index zu erstellen, wie folgt:

CREATE INDEX ON foo (id, date DESC NULLS LAST, value); 

die Planer wählen Index Only Scan zu verwenden, aber wenn ich es tue es auf diese Weise:

CREATE INDEX ON foo (id, date DESC NULLS LAST, COALESCE(value, 0)); 

der Planer tut gerade Index Scan.

Warum? Ich versuche, die Kosten der Auswertung der COALESCE Funktion während der Ausführung der Abfrage zu vermeiden. Warum funktioniert es nicht mit Index Only Scan?

+2

Die "Kosten" von 'coalesce()' ist fast Null, mach dir keine Sorgen darüber. –

Antwort

1

Ich denke, Sie haben zu Unrecht angenommen, dass COALESCE(value, 0) in Ihrer SELECT in Bezug auf eine Indexnutzung zählt. Um die Wahrheit zu sagen, ist nur die Umwandlung der Transformation nach die Zeilenwerte zurückgegeben.

Was eine Indexnutzung betrifft, ist Ihre WINDOW FUNCTION. Zuerst partitionieren Sie durch id und zweitens bestellen Sie Werte in jeder Partition von date DESC NULLS LAST. Diese zwei Dinge bestimmen, dass Index wie CREATE INDEX ON foo (id, date DESC NULLS LAST, ...) nützlich ist, was auch immer Sie in den nächsten Positionen setzen. Beachten Sie, dass PostgreSQL den Index überhaupt nicht verwendet, wenn Sie die Reihenfolge id und date bei der Indexerstellung ändern.

Nun müssen Sie wissen, dass INDEX ONLY SCAN nur verwendet werden kann, wenn der Index selbst die gesamten unberührten Zeilenwerte speichert, die von der Abfrage angefordert wurden. Nach PostgreSQL manual:

Wenn der Index speichert die ursprünglichen indizierten Datenwerte (und nicht eine verlustbehaftete Darstellung von ihnen), ist es sinnvoll Index-Only-Scans zu unterstützen, in denen der Index der aktuellen Daten zurückgibt ...

In Ihrem Fall Ihre seccond Index speichert einige verlustbehaftete Darstellung eine Zeile, weil der Wert letzter Spalte durch eine Funktion und Abfrage für id, value und date fragt umgewandelt wird. PostgreSQL ist nicht so intelligent zu sehen, dass es nur NULLs von 0 Substitution ist. Für ihn ist es nicht der ursprüngliche Wert. Also müssen wir auf die Tabelle zugreifen, um die ursprünglichen Zeilenwerte zu erhalten (am Ende verwenden wir einfach). Danach werden Werte für die Ausgabe formatiert und COALESCE(values, 0) passiert.

Edit:

Ich denke, diese Erklärung zu Ihnen so weit wie Ihre Frage über Interna ausreichend ist, betroffen ist. Um über COALECE() Auswertungskosten zu sprechen, stimme ich mit a_horse_with_no_name überein, dass Sie sich wahrscheinlich nicht darum kümmern sollten.

+0

Ah, ich wüsste nicht, dass das Holen zuerst passiert im Falle von 'COALESCE', wenn ich das so sagen kann ... –

+0

Da beantwortete ich Sie genau Fragen und erklärte Dinge, und Sie lernen etwas, was Sie nicht wussten (wie Du hast es gesagt) Warum akzeptierst du meine Antwort nicht? –

+0

Vergessen Sie einfach, dies zu tun ... –