Mysql 如何将地理位置查询与其他条件相结合
我有两个疑问:Mysql 如何将地理位置查询与其他条件相结合,mysql,sql,join,geolocation,inner-join,Mysql,Sql,Join,Geolocation,Inner Join,我有两个疑问: SELECT (ACOS(least(1,COS(0.4878295615756141)*COS(-1.4391492410217162)*COS(RADIANS(places.lat))*COS(RADIANS(places.lng))+ COS(0.4878295615756141)*SIN(-1.4391492410217162)*COS(RADIANS(places.lat))*SIN(RADIANS(places.lng))+
SELECT
(ACOS(least(1,COS(0.4878295615756141)*COS(-1.4391492410217162)*COS(RADIANS(places.lat))*COS(RADIANS(places.lng))+
COS(0.4878295615756141)*SIN(-1.4391492410217162)*COS(RADIANS(places.lat))*SIN(RADIANS(places.lng))+
SIN(0.4878295615756141)*SIN(RADIANS(places.lat))))*3963.1899999999996)
AS distance, places.*
FROM `places`
WHERE ((
(ACOS(least(1,COS(0.4878295615756141)*COS(-1.4391492410217162)*COS(RADIANS(places.lat))*COS(RADIANS(places.lng))+
COS(0.4878295615756141)*SIN(-1.4391492410217162)*COS(RADIANS(places.lat))*SIN(RADIANS(places.lng))+
SIN(0.4878295615756141)*SIN(RADIANS(places.lat))))*3963.1899999999996)
<= 200.0))
其工作原理如下:第一个查询查找指定半径内的位置。第二个查询查找拥有在第一个查询中找到的地点的所有公司
第二个查询中的部分-(1,3,6,…)
-在ruby中,我从places中获取所有公司id
,并将它们放入第二个查询(公司id
是places
表的一个属性)
我试图将这两个查询合并为一个查询,因为我想按距离对公司进行排序(如果离给定点最近的位置属于“公司A”,那么该公司将在输出中处于第一位),并且作为查询的结果,我试图接收:
- 在给定半径内有位置的公司
- 属于公司的地方以及这些地方在指定半径内
SELECT places.*, companies.*,
69.0 * HAVERSINE(places.lat, places.lng, 27.950575,-82.45717) AS distance
FROM places
JOIN companies ON companies.id = places.company_id
JOIN service_areas ON service_areas.company_id = companies.id
WHERE places.lat BETWEEN 27.950575 - (200.0 / 69.0)
AND 27.950575 + (200.0 / 69.0)
AND places.lng BETWEEN -82.45717 - (200.0 / (69.0 * COS(RADIANS(27.950575))))
AND -82.45717 + (200.0 / (69.0 * COS(RADIANS(27.950575))))
AND companies.id = places.company_id
AND service_areas.state_name = 'CA'
ORDER BY distance
我还在places.lat
和places.lng
列中添加了索引。当我在MySQL控制台中运行这个查询时,我得到了586个结果;当我第二次运行30秒,第三次运行18秒时,查询持续了1分22秒
我只是在分析收到的结果,以验证我所需要的
EDIT2:
当我深入查看获取的结果时,我发现查询加载的是公司
,但总是没有位置
。我认为对于特定的搜索没有位置
,所以我更改了城市等等,但是查询仍然没有返回位置
因此,我尝试单独运行查询,如下所示:
SELECT places.*,
69.0 * HAVERSINE(places.lat,places.lng, 27.950575,-82.45717) AS distance
FROM places
WHERE places.lat
BETWEEN 27.950575 - (200 / 69.0)
AND 27.950575 + (200 / 69.0)
AND places.lng
BETWEEN -82.45717 - (200 / (69.0 * COS(RADIANS(27.950575))))
AND -82.45717 + (200 / (69.0 * COS(RADIANS(27.950575))))
这个查询返回6600个位置,查询持续了30秒。我尝试更改“big”查询中的JOIN
s的顺序,希望这可能会导致没有提取位置
,但没有帮助,仍然没有加载位置。我想知道是什么导致了这个问题
编辑3:
甚至尝试这样做(省去服务区域
表上的位置
,目的是调试它并找出为什么查询从未返回任何位置
):
结果是有5000多家未经筛选的公司,但仍然没有地方
谢谢你看来你有两个问题
使此查询高效
使用距离计算在位置
表中查找内容,并将其与其他表中的内容关联
看起来您在27.950575,-82.45717(以度为单位)的特定位置使用常量值。如果那是美国佛罗里达州坦帕市中心扎克街的一个位置,我对你常数的含义猜对了。让我们调用这些值latpoint
和lonpoint
另一个常数3963.19告诉我们你的单位是英里。每度有69英里
为了开始解决这个问题而不陷入数学洪流,让我们假设存在一个名为
HAVERSINE(lat1,long1, lat2,long2)
可在此处找到此功能:
这样我们就可以轻松地构建代码,让自己相信我们拥有正确的代码
您的第一个查询可以使用一些漂亮的WHERE子句进行优化:
places.lat BETWEEN latpoint - (200.0 / 69.0)
AND latpoint + (200.0 / 69.0)
AND places.lon BETWEEN lonpoint - (200.0 / (69.0 * COS(RADIANS(latpoint))))
AND lonpoint + (200.0 / (69.0 * COS(RADIANS(latpoint))))
这些条款在你的起点周围划出一个200英里的边界。他们可以非常有效地使用表中(lat,lon)
上的索引
因此,这将是您修改后的距离计算查询
SELECT places.*,
69.0 * HAVERSINE(places.lat,places.lon, latpoint,lonpoint) AS distance
FROM places
WHERE places.lat BETWEEN latpoint - (200.0 / 69.0)
AND latpoint + (200.0 / 69.0)
AND places.lon BETWEEN lonpoint - (200.0 / (69.0 * COS(RADIANS(latpoint))))
AND lonpoint + (200.0 / (69.0 * COS(RADIANS(latpoint))))
ORDER BY distance
LIMIT 50
因为WHERE
子句很有希望地消除了places
表中的许多行,这将节省大量时间。有关更完整的说明,请参见:
现在我们已经准备好了一个高效查询的框架,我需要做一个假设。这就是:你可以做这个加入
... places
JOIN companies ON companies.id = places.company_id
因此,将这些内容添加到查询中变得非常容易已编辑,其中包含有关公司
和位置
表如何关联的信息
SELECT places.*, companies.*,
69.0 * HAVERSINE(places.lat,places.lon, latpoint,lonpoint) AS distance
FROM places
JOIN companies ON companies.id = places.company_id
JOIN service_areas ON companies.id = service_areas.company_id
WHERE places.lat BETWEEN latpoint - (200.0 / 69.0)
AND latpoint + (200.0 / 69.0)
AND places.lon BETWEEN lonpoint - (200.0 / (69.0 * COS(RADIANS(latpoint))))
AND lonpoint + (200.0 / (69.0 * COS(RADIANS(latpoint))))
AND companies.id IN (1, 3, 6, ...) AND service_areas.state_name = 'CA'
ORDER BY distance
LIMIT 50
这将找到所有在加利福尼亚州设有服务区的公司,这些服务区距离您的latpoint、lonpoint
位置(恰好位于佛罗里达州)不到200英里
在位置上的复合索引
:(公司id、lat、lon)
可以提高此查询的性能
如果要使用距离标准以避免混淆,您可能需要省略state\u name
标准。该代码需要稍微清理一下。我不知道什么东西在哪里。@padagome我同意第一个查询很难阅读,但这是因为有一些半径和测角函数的计算(不确定如何使可读性更好)。第二个是带有连接的“简单”查询。@Padagomez-我尽了最大努力……您的第一个查询是球形余弦公式(通常称为Haversine公式)的一个版本吗?该公式中的常数(例如0.4878295615756141
)是什么意思?ypurplaces
table和您第二次查询中提到的表如何相互关联?谢谢@PM77-1,我会看看我能从那里做些什么。杀手级答案@Ollie Jones找到了一个低得多的解决方案。对于以上所有详细的查询和公式优化解释,我们感到非常荣幸。我只是想根据ID建议第二次加入公司并放置表。。。说它好:-PHello@OllieJones,非常感谢你提供了非常详细的答案,我很感激。当我研究解决方案时,问题是
... places
JOIN companies ON companies.id = places.company_id
SELECT places.*, companies.*,
69.0 * HAVERSINE(places.lat,places.lon, latpoint,lonpoint) AS distance
FROM places
JOIN companies ON companies.id = places.company_id
JOIN service_areas ON companies.id = service_areas.company_id
WHERE places.lat BETWEEN latpoint - (200.0 / 69.0)
AND latpoint + (200.0 / 69.0)
AND places.lon BETWEEN lonpoint - (200.0 / (69.0 * COS(RADIANS(latpoint))))
AND lonpoint + (200.0 / (69.0 * COS(RADIANS(latpoint))))
AND companies.id IN (1, 3, 6, ...) AND service_areas.state_name = 'CA'
ORDER BY distance
LIMIT 50