2012-12-28 6 views
5

Die folgende Abfrage gibt die Austragungsorte in der Nähe von uns (lat: 62,0, lon: 25,0) innerhalb dessen Radius fallen wir in nach Entfernung:Wie Ergebnis für SELECT, WHERE und ORDER BY-Klauseln wiederverwenden?

SELECT *, 
    earth_distance(ll_to_earth(62.0, 25.0), 
    ll_to_earth(lat, lon)) AS distance 
FROM venues 
WHERE earth_distance(ll_to_earth(62.0, 25.0), ll_to_earth(lat, lon)) <= radius 
ORDER BY earth_distance(ll_to_earth(62.0, 25.0), ll_to_earth(lat, lon)) 

Ist es möglich (und ratsam) aus dem Ergebnis wiederverwenden earth_distance(ll_to_earth(62.0, 25.0), ll_to_earth(lat, lon)) statt separat für SELECT, WHERE und ORDER BY Klauseln zu berechnen?

+3

Ich denke, wenn die Funktion als [unveränderbar] (http://www.postgresql.org/docs/9.2/static/xfunc-volatility.html) markiert ist, wird das Ergebnis wiederverwendet werden. Hoffentlich kann ein Postgres-Experte mich korrigieren, wenn ich falsch liege. –

+2

@MikeChristensen: Ja, so funktioniert es normalerweise. Sogar 'STABLE' ist genug, weil das das Ergebnis in einer einzigen Anweisung als konstant deklariert."IMMUTABLE" ist erforderlich, um selbst zwischen * Transaktionen konstante Ergebnisse zu erzielen. Dies ist erforderlich, damit eine Funktion beispielsweise in einem Index verwendet werden kann. –

Antwort

4

In Die GROUP BY und ORDER BY Klausel können Sie auf Spalten Aliase (Ausgabespalten) oder sogar Ordnungsnummern von SELECT Listenelemente verweisen. Ich zitiere the manual on ORDER BY:

Jeder Ausdruck der Name oder Ordnungszahl einer Ausgabespalte sein kann (SELECT-Liste Artikel), oder es kann ein beliebiger Ausdruck von Eingangsspaltenwerten sein.

Fett Hervorhebung meins.

Aber in den WHERE und HAVING Klauseln können Sie nur auf Spalten aus den Basistabellen (Eingabespalten) verweisen, so dass Sie Ihren Funktionsaufruf buchstabieren müssen.

SELECT *, earth_distance(ll_to_earth(62.0, 25.0), ll_to_earth(lat, lon)) AS dist 
FROM venues 
WHERE earth_distance(ll_to_earth(62.0, 25.0), ll_to_earth(lat, lon)) <= radius 
ORDER BY distance; 

Wenn Sie wollen wissen, ob es schneller ist die Berechnung in eine CTE oder Unterabfrage zu packen, testen Sie es einfach mit EXPLAIN ANALYZE. (Ich bezweifle es.)

SELECT * 
FROM (
    SELECT * 
     ,earth_distance(ll_to_earth(62.0, 25.0), ll_to_earth(lat, lon)) AS dist 
    FROM venues 
    ) x 
WHERE distance <= radius 
ORDER BY distance; 

Wie @Mike commented, durch eine Funktion erklärt STABLE (oder IMMUTABLE) können Sie die Anfrageplaner informieren, die von einem Funktionsaufruf führen kann mehrfach für identische Anrufe innerhalb einer einzigen Anweisung wiederverwendet werden. Ich zitiere the manual here:

A STABLE Funktion die Datenbank nicht ändern kann und zu Rückkehr der gleichen Ergebnisse die gleichen Argumente für alle Zeilen innerhalb einer einzigen Anweisung gegeben garantiert. Diese Kategorie ermöglicht es dem Optimierer mehrere Aufrufe der Funktion zu einem einzigen Anruf zu optimieren.

Fett Hervorhebung meins.

+0

Große Antwort. Nur um klar zu sein: Sie bezweifeln, dass Unterabfrage schneller als CTE sein wird oder bezweifeln, dass es sich lohnt, CTE/Unterabfrage zu verwenden, anstatt es nur zweimal zu berechnen? – randomguy

+1

@randomguy: Ich erwarte, dass die einfache Form schneller ist als entweder mit Unterabfrage oder CTE. Aber nur testen und sehen. Testen >> Raten. –

+1

@ErwinBrandstetter: Meiner Erfahrung nach werden CTEs immer intakt gehalten, auch wenn sie nur einmal referenziert werden (wobei IMO dem äquivalenten VIEW- oder 'FROM (subquery) -Form entsprechen sollte). Folglich werden sie nicht vom Plangenerator demontiert und wieder zusammengesetzt, was zu suboptimalen Plänen führen kann. Auf der Plus-Seite: Dies wird die Rangtables relativ klein halten (und ziemlich unabhängig davon), was die kombinatorische Explosion in der Anzahl der möglichen Pläne reduzieren wird. (Ich habe 9.2 noch nicht versucht) – wildplasser

3

Während ich hauptsächlich MS SQL Server verwende, bin ich ziemlich sicher, dass PostgreSQL CTEs unterstützt. Versuchen Sie so etwas wie:

WITH CTE_venues AS (
SELECT *, earth_distance(ll_to_earth(62.0, 25.0), ll_to_earth(lat, lon)) AS distance 
FROM venues 
) 
SELECT * 
FROM CTE_venues 
WHERE distance <= radius 
ORDER BY distance 
0

Sie auch alleine stehen erstellen oder verpackt Funktion und verwenden Sie es in Ihre Fragen:

SELECT * 
    FROM ... 
    WHERE distance <= your_function() -- OR your_package_name.your_function() 
ORDER BY ... 

Sie Ihre Funktion in ausgewählten verwenden können:

Select your_function() 
    From your_table... 
Where ... 
+2

Völlig nutzlose Antwort. 1) Die berechnete Entfernung muss immer noch in der Select-Klausel sein, dh 'Select *' wird nicht funktionieren. 2) Der Ergebnissatz muss immer noch vom Funktionsergebnis geordnet werden. 3) Dies entspricht einer Abfrage, die sich absolut nicht von dem unterscheidet, was ursprünglich gepostet wurde. Die Frage betrifft die Funktionsflüchtigkeit und ob der Abfrageoptimierer die Funktion dreimal oder einmal auswertet. –

+1

Nur versuchen, zu helfen ... Bitte schauen Sie sich mein Beispiel noch einmal an - Funktionen können in ausgewählten verwendet werden ... Nur zur Info ... Und die Frage war, die Ergebnisse wieder zu verwenden. Funktion gibt ein Ergebnis zurück und kann wiederverwendet werden ... – Art