请原谅这个尴尬的头衔。我很难将我的问题提炼成一个短语。如果有人能想出更好的,请随意。
我有以下简化架构:
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
我现在无法找到重复订单/限制条款的方法。我最初尝试将它作为“{1}}字段中的”距离“别名,但是psql告诉我这个别名在SELECT
子句中不可用。精细。如果在这周围有一些花哨的裤子,我会全力以赴,但在我的主要问题上:
我想要做的是返回供应商列表,每个供应商都与最接近的位置相关联,并按照邻近度排列此列表并受半径限制。
假设我有2个供应商,每个供应商有两个位置。我想要一个限制半径的查询,使得四个位置中只有一个位于其中,以便将该位置的相关供应商与供应商本身一起返回。如果半径包含所有位置,我希望供应商1在其位置与供应商2之间最接近,其位置最接近,最终根据最近位置的距离对供应商1和2进行排序。
在MySQL中,我设法使用WHERE
然后GROUP BY
获取每个供应商行中最近的位置。但PostgreSQL似乎对MIN(distance)
的使用更为严格。
如果可能的话,我希望避免插入GROUP BY
条款。如果可能的话,我还想重复使用上述查询的SELECT
和WHERE
部分。但这绝不是绝对的要求。
我曾在ORDER
和DISTINCT ON
做过陈词滥调,但这些给我带来了一些麻烦,主要是因为我在其他地方遗漏了镜像陈述,我不会详细详细说明现在。
我最终采用了基于OMG Ponies' excellent answer的解决方案。
GROUP BY
与OMG Ponies解决方案的一些偏差:
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;
进行多态关联。一点前提改变。答案 0 :(得分:2)
对于PostgreSQL 8.4+,您可以使用analytics like ROW_NUMBER:
SELECT x.*
FROM (SELECT v.*,
t.*,
ABS(t.latitude - 30) + ABS(t.longitude - 30) AS distance,
ROW_NUMBER() OVER(PARTITION BY v.id
ORDER BY ABS(t.latitude - 30) + ABS(t.longitude - 30)) AS rank
FROM VENDORS v
JOIN LOCATIONS t ON t.vendor_id = v.id
WHERE t.latitude IS NOT NULL
AND t.longitude IS NOT NULL) x
WHERE x.rank = 1
AND x.distance < 50
ORDER BY x.distance
我离开过滤距离,如果排名最高的值超过50,那么供应商就不会出现。如果您不希望这种情况发生,请删除小于50分的距离检查。
ROW_NUMBER将返回一个不同的顺序值,该值会针对此示例中的每个供应商进行重置。如果你想要重复,你需要看看使用DENSE_RANK。
请参阅this article for emulating ROW_NUMBER on PostgreSQL pre-8.4。
答案 1 :(得分:1)
MySQL扩展了GROUP BY,并非所有列都需要聚合。 http://dev.mysql.com/doc/refman/5.0/en/group-by-hidden-columns.html
我在这里遇到过很多同样问题的问题。诀窍是在子查询中获取nececssary列,然后在外部查询中自己加入它:
create temp table locations (id int, vender_id int, latitude int, longitude int);
CREATE TABLE
insert into locations values
(1, 1, 50, 50),
(2, 1, 35, 30),
(3, 2, 5, 30)
;
SELECT
locations.*, distance
FROM
(
SELECT
vender_id,
MIN(ABS(latitude - 30) + ABS(longitude - 30)) as distance
FROM locations
WHERE latitude IS NOT NULL AND longitude IS NOT NULL
GROUP BY vender_id
) AS min_locations
JOIN locations ON
ABS(latitude - 30) + ABS(longitude - 30) = distance
AND min_locations.vender_id = locations.vender_id
WHERE distance < 50
ORDER BY distance
;
id | vender_id | latitude | longitude | distance
----+-----------+----------+-----------+----------
2 | 1 | 35 | 30 | 5
3 | 2 | 5 | 30 | 25