請原諒尷尬的標題。我很難將我的問題提煉成一個短語。如果任何人都可以拿出更好的一個,那就隨意吧。如何根據「許多」中的某個標準基於一對多關聯查詢對結果進行分組?
我有以下簡單的模式:
vendors
INT id
locations
INT id
INT vendor_id
FLOAT latitude
FLOAT longitude
我完全有能力返回最近的銷售商,由接近排序列表,按半徑的近似限制的:
SELECT * FROM locations
WHERE latitude IS NOT NULL AND longitude IS NOT NULL
AND ABS(latitude - 30) + ABS(longitude - 30) < 50
ORDER BY ABS(latitude - 30) + ABS(longitude - 30) ASC
我在這個時候不能在重複訂單/限制期限的時候找到解決方法。我最初嘗試在SELECT
字段中將其作爲「距離」進行別名,但psql告訴我該別名在WHERE
子句中不可用。精細。如果有一些花哨的褲子的方式圍繞這一點,我全部耳朵,但在我的主要問題:
我想要做的是返回供應商的列表,每個供應商與其最近的位置,並且按照接近度排序並且以半徑限制該列表。
所以假設我有2個供應商,每個供應商有兩個位置。我想要一個限制半徑的查詢,以便只有四個位置中的一個位於其中,以便將該位置的關聯供應商與供應商一起返回。如果半徑包含所有位置,我希望供應商1提供其位置與供應商2之間距離最近的供應商2最近,最終根據其最近位置的距離排序供應商1和供應商2。
在MySQL中,我設法通過使用GROUP BY
然後MIN(distance)
來獲得每個供應商行中最近的位置。但PostgreSQL似乎更嚴格的使用GROUP BY
。
如果可能,我希望避免插手SELECT
條款。我還想,如果可能的話,重新使用上述查詢的WHERE
和ORDER
部分。但這絕不是絕對的要求。
我對DISTINCT ON
和GROUP BY
做了陳腐的嘗試,但是這些給我帶來了一些麻煩,主要是因爲我在其他地方缺少鏡像語句,我現在不會詳細說明。
解
我結束了採用基於截止OMG Ponies' excellent answer的溶液。
SELECT vendors.* FROM (
SELECT locations.*,
ABS(locations.latitude - 2.1) + ABS(locations.longitude - 2.1) AS distance,
ROW_NUMBER() OVER(PARTITION BY locations.locatable_id, locations.locatable_type
ORDER BY ABS(locations.latitude - 2.1) + ABS(locations.longitude - 2.1) ASC) AS rank
FROM locations
WHERE locations.latitude IS NOT NULL
AND locations.longitude IS NOT NULL
AND locations.locatable_type = 'Vendor'
) ranked_locations
INNER JOIN vendors ON vendors.id = ranked_locations.locatable_id
WHERE (ranked_locations.rank = 1)
AND (ranked_locations.distance <= 0.5)
ORDER BY ranked_locations.distance;
從OMG小馬的解決方案有些不同之處:
- 位置,現在通過多態相關
_type
。一點前提變化。 - 我將連接移到了子查詢之外。我不知道是否存在性能影響,但我認爲將子查詢視爲獲取位置和分區排名,然後將更大的查詢視爲將它們集合在一起的行爲是有意義的。
- 未成年人帶走表名別名。雖然我很多時候都習慣於鋸齒,但它讓我更難以跟隨。我會等到我對PostgreSQL有更多的經驗之後纔開始工作。
表名走樣(大部分)的風格一點:有些人總是使用它們,有些人避免它們。將計算移動到內部查詢中,然後再與其他數據「後來」結合起來對我來說是完全有意義的。查看解釋輸出是查看是否存在性能影響的方法;我覺得在這種情況下,它可能會提供一個小的改進,因爲在排序功能所隱含的排序期間不得不保持較少的數據。 – araqnid 2011-02-07 11:42:10