2012-12-28 14 views
5

La seguente query restituisce i luoghi vicino a noi (Lat: 62.0, Lon: 25.0) all'interno cui raggio cadiamo in ordine di distanza:Come riutilizzare i risultati per le clausole SELECT, WHERE e ORDER BY?

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)) 

E 'possibile (e consigliabile) riutilizzare il risultato da earth_distance(ll_to_earth(62.0, 25.0), ll_to_earth(lat, lon)) invece di calcolarlo separatamente per le clausole SELECT, WHERE e ORDER BY?

+3

Penso che se la funzione è contrassegnata come [immutable] (http://www.postgresql.org/docs/9.2/static/xfunc-volatility.html), il risultato verrà riutilizzato. Spero che un esperto di Postgres possa correggermi se sbaglio. –

+2

@MikeChristensen: Sì, è così che di solito funziona. Anche 'STABLE' è sufficiente, perché dichiara la costante di risultato all'interno di una singola istruzione.'IMMUTABLE' è richiesto per affermare risultati costanti anche * tra * transazioni. È necessario che una funzione sia utilizzabile in un indice, ad esempio. –

risposta

4

In la clausola GROUP BY e ORDER BY si può fare riferimento a alias di colonne (colonne di output) o anche numeri ordinali di voci di elenco SELECT. Cito the manual on ORDER BY:

Ogni espressione può essere il nome o numero ordinale di una colonna di output (elemento dell'elenco SELECT), oppure può essere un'espressione arbitraria formata da valori input-colonna.

Bold enfasi miniera.

Ma nelle clausole WHERE e HAVING, è possibile fare riferimento solo alle colonne delle tabelle di base (colonne di input), quindi è necessario specificare la chiamata di funzione.

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; 

Se vuoi sapere se è più veloce per confezionare il calcolo in una sottoquery CTE o, semplicemente prova con EXPLAIN ANALYZE. (Ne dubito.)

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; 

Come @Mike commented, dichiarando una funzione STABLE (o IMMUTABLE) si informa il pianificatore query che deriva da una chiamata di funzione può essere riutilizzato più volte per le chiamate identici all'interno di una singola istruzione. Cito the manual here:

Una funzione stabile non può modificare il database ed è garantito per ritorno gli stessi risultati riportati gli stessi argomenti per tutte le righe all'interno di una singola istruzione . Questa categoria consente all'ottimizzatore di ottimizzare più chiamate della funzione a una singola chiamata.

Bold enfasi miniera.

+0

Ottima risposta. Giusto per essere chiari: dubiti che la sottoquery sarà più veloce di CTE o dubito che valga la pena usare CTE/sottoquery semplicemente calcolandone il doppio? – randomguy

+1

@randomguy: mi aspetto che il modulo semplice sia più veloce rispetto a subquery o CTE. Ma prova e vedi. Prova >> indovinando. –

+1

@ErwinBrandstetter: nella mia esperienza i CTE sono sempre mantenuti intatti, anche se riferiti una sola volta (che IMO dovrebbe essere equivalente al modulo VIEW o 'FROM (subquery)' equivalente). Di conseguenza, non verranno smontati + riassemblati dal generatore di piani, che * potrebbe * portare a piani non ottimali. Sul lato positivo: ciò manterrà i rangetables relativamente piccoli (e piuttosto non correlati), il che ridurrà l'esplosione combinatoria nel numero di possibili piani. (Non ho ancora provato il 9.2) – wildplasser

3

Mentre utilizzo principalmente MS SQL Server, sono abbastanza sicuro che PostgreSQL supporti le CTE. Provare qualcosa di simile:

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

È inoltre possibile creare stand alone o funzione confezionato e utilizzarlo nelle query:

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

È possibile utilizzare la funzione di selezionare:

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

Risposta completamente inutile. 1) La distanza calcolata deve ancora essere nella clausola select, ovvero 'select *' non funzionerà. 2) il set di risultati deve ancora essere ordinato dal risultato della funzione. 3) Ciò equivale a una query assolutamente diversa da quella originariamente pubblicata. La domanda riguarda la volatilità delle funzioni e se il Query Optimizer valuta la funzione tre volte o una. –

+1

Solo cercando di aiutare ... Per favore guarda ancora il mio esempio - le funzioni possono essere usate in select ... Solo FYI ... E la domanda riguardava il riutilizzo dei risultati. La funzione restituisce un risultato e può essere riutilizzato ... – Art

Problemi correlati