2012-12-28 12 views
5

Następująca kwerenda zwraca miejsca w pobliżu nas (łac: 62,0, dł: 25,0), wewnątrz którego promień wpadamy w uporządkowane według odległości:Jak ponownie wykorzystać wynik dla klauzul SELECT, WHERE i 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)) 

Czy jest możliwe (i zalecane), aby ponownie wykorzystać wynik z earth_distance(ll_to_earth(62.0, 25.0), ll_to_earth(lat, lon)) zamiast obliczać osobno dla klauzul SELECT, WHERE i ORDER BY?

+3

Myślę, że jeśli funkcja jest oznaczona jako [immutable] (http://www.postgresql.org/docs/9.2/static/xfunc-volatility.html), wynik zostanie ponownie wykorzystany. Mam nadzieję, że ekspert Postgresu może mnie poprawić, jeśli się mylę. –

+2

@MikeChristensen: Tak, tak to zwykle działa. Nawet 'STABLE' jest wystarczający, ponieważ deklaruje stałą wyniku w pojedynczej instrukcji.'IMMUTABLE' jest wymagane, aby potwierdzić stałe wyniki nawet * pomiędzy * transakcjami. Jest to potrzebne, aby funkcja była użyteczna na przykład w indeksie. –

Odpowiedz

4

W klauzula GROUP BY i ORDER BY można odwoływać się do kolumn aliasów (kolumn wyjściowych), a nawet numerów porządkowych elementów listy SELECT. Cytuję the manual on ORDER BY:

Każde wyrażenie może być nazwą lub numer porządkowy kolumny wyjściowej (Wybierz element listy), lub może to być dowolny wyraz utworzony z wartości nakładów i kolumn.

Pogrubiony nacisk mój.

Jednak w klauzulach WHERE i HAVING można odwoływać się tylko do kolumn z tabel podstawowych (kolumn wejściowych), więc trzeba przeliterować swoje wywołanie funkcji.

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; 

Jeśli chcesz wiedzieć, czy to szybciej się pakować obliczenia w CTE lub podzapytania, po prostu przetestować go EXPLAIN ANALYZE. (Wątpię.)

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; 

Jak @Mike commented, deklarując funkcję STABLE (lub IMMUTABLE) poinformowania planera kwerendy, która jest wynikiem wywołania funkcji mogą być używane wielokrotnie dla tych samych połączeń w ramach jednego rachunku. Cytuję the manual here:

STABILNY funkcja nie może modyfikować bazę danych i gwarantuje takie same wyniki podane te same argumenty dla wszystkich wierszy w pojedynczej instrukcji. Ta kategoria umożliwia optymalizatorowi zoptymalizowanie wielu wywołań funkcji do pojedynczego wywołania.

Pogrubiony nacisk mój.

+0

Świetna odpowiedź. Dla jasności: wątpisz, że podkwerenda będzie szybsza od CTE, czy wątpisz, czy warto używać CTE/podkwerendy zamiast po prostu go dwa razy obliczać? – randomguy

+1

@randomguy: Oczekuję, że zwykła forma będzie szybsza niż z podzapytaniem lub CTE. Ale po prostu sprawdź i zobacz. Testowanie >> Zgadywanie. –

+1

@ErwinBrandstetter: w moim odczuciu CTE zawsze pozostają nienaruszone, nawet gdy są przywoływane tylko raz (które IMO powinny być równoważne z równoważnym VIEW lub 'Z (podzapytanie)' formularzem). W rezultacie nie zostaną zdemontowane i ponownie zmontowane przez generator planów, co * może * prowadzić do nieoptymalnych planów. Na plus: to utrzyma łączniki stosunkowo małe (i raczej niepowiązane), co zmniejszy kombinatoryczną eksplozję w liczbie możliwych planów. (Jeszcze nie próbowałem 9.2) – wildplasser

3

Podczas gdy używam głównie MS SQL Server, jestem całkiem pewny, że PostgreSQL obsługuje CTE. Spróbuj czegoś takiego:

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

Można również tworzyć sam stojak lub funkcję pakowane i używać go w zapytaniach:

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

można używać funkcji w select:

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

Całkowicie bezużyteczna odpowiedź. 1) Obliczona odległość nadal musi znajdować się w klauzuli select, tzn. 'Select *' nie będzie działać. 2) zestaw wyników wciąż musi być uporządkowany przez wynik funkcji. 3) Jest to równoznaczne z zapytaniem absolutnie nie różniącym się od pierwotnie zamieszczonego. Pytanie dotyczy zmienności funkcji i tego, czy optymalizator zapytań ocenia funkcję trzykrotnie lub raz. –

+1

Po prostu próbuję pomóc ... Proszę spojrzeć na mój przykład ponownie - funkcje mogą być używane w wybranych ... Po prostu FYI ... I pytanie dotyczyło ponownego wykorzystania wyników. Funkcja zwraca wynik i można go ponownie wykorzystać ... – Art

Powiązane problemy