2012-12-28 72 views
5

下面的查詢返回在我們附近的場地(緯度:25.0:62.0,經度)內,其半徑我們墜入按距離排序:如何重新使用SELECT,WHERE和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)) 

是否有可能(和建議)重新使用從結果earth_distance(ll_to_earth(62.0, 25.0), ll_to_earth(lat, lon))而不是分別爲SELECT,WHERE和ORDER BY子句計算它?

+3

我想如果函數被標記爲[immutable](http://www.postgresql.org/docs/9.2/static/xfunc-volatility.html),結果將被重新使用。希望Postgres專家能糾正我,如果我錯了。 –

+2

@MikeChristensen:是的,這就是它通常的工作原理。即使'STABLE'也足夠了,因爲它在單個語句中聲明瞭結果常量。* IMMUTABLE'需要在*事務之間聲明不變的結果。例如,這需要一個函數在索引中可用。 –

回答

4

GROUP BYORDER BY子句可以引用列別名(輸出列)或者甚至是序號爲SELECT的列表項。我引述the manual on ORDER BY

每個表達式可以是名稱或序號輸出列的 (選擇列表項),或者它可以是從 輸入列值構成的任意表達式。

大膽重視我的。

但是在WHEREHAVING子句中,只能引用基表(輸入列)中的列,所以必須拼出函數調用。

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; 

如果你想知道,如果它的速度更快的計算包成CTE或子查詢,只需用EXPLAIN ANALYZE測試。 (我很懷疑。)

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; 

@Mike commented,通過聲明一個功能STABLE(或IMMUTABLE)你通知查詢規劃,從一個函數調用可以爲一個語句中使用相同的電話多次重複使用造成的。我引用the manual here

一個穩定的函數不能修改數據庫並且保證 回報給予 一個語句中所有行相同的參數相同的結果。此類別允許優化器將該函數的多個調用優化爲單次調用

大膽重視我的。

+0

很好的答案。要明確一點:您懷疑子查詢會比CTE更快還是懷疑使用CTE /子查詢而不是僅僅計算兩次是值得的? – randomguy

+1

@randomguy:我期望普通表格比使用子查詢或CTE更快。但只是測試看看。測試>>猜測。 –

+1

@ErwinBrandstetter:根據我的經驗,CTE始終保持完整,即使只引用一次(其中IMO應等同於等效的VIEW或FROM(子查詢)形式)。因此,它們不會被計劃生成器分解或重新組合,這可能導致次優計劃。正面:這將使範圍表保持相對較小(而不相關),這將減少可能計劃數量的組合爆炸。 (我還沒有試過9.2) – wildplasser

3

雖然我主要使用MS SQL Server,但我非常確定PostgreSQL支持CTE。嘗試是這樣的:

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

您也可以單獨創建獨立或打包的函數,並用它在你的查詢:

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

您可以使用在選擇你的函數:

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

完全無用的答案。 1)計算的距離仍然需要在select子句中,即'select *'不起作用。 2)結果集仍然必須由函數結果排序。 3)這等於查詢與原始發佈完全沒有區別。問題是關於函數波動性,以及查詢優化器是評估函數三次還是一次。 –

+1

只是想幫助...請再次看看我的例子 - 函數可以用於選擇...只是FYI ...而問題是關於重用結果。函數返回一個結果,可以重用... – Art

相關問題