我有这两个问题:
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))
和
SELECT `companies`.*
FROM `companies`
INNER JOIN `service_areas` ON `service_areas`.`company_id` = `companies`.`id`
WHERE `companies`.`id` IN (1, 3, 6, ...) AND `service_areas`.`state_name` = 'CA'
它的工作方式如下:第一个查询查找指定半径内的位置。第二个查询查找拥有第一个查询中找到的地点的所有公司。
第二个查询中的部分 - (1, 3, 6, ...)
- 在ruby中,我从地点获取所有company_id
并将它们放到第二个查询中(company_id
是一个属性places
表格。)
我试图将这两个查询合并为一个,因为我想按distance
对公司进行排序(如果最接近给定点的位置属于&#34;公司A&#34; ,所以这家公司将在输出中排在第一位。作为查询的结果,我试图收到:
这似乎有点超出了我的联盟,我试图将这两个查询合并为一个,因为有两个查询,我必须使用Ruby进行一些操作(过滤地点)并且这些操作最后来自60-90秒......
提前感谢你们的时间。
修改 我稍微修改了一下查询,这里是它的外观:
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个结果;查询持续了1分22秒,当时我第二次跑30秒,第三次尝试18秒。
我只是在分析收到的结果,以验证我的需要。
EDIT2:
当我更深入地查看获取的结果时,我发现查询加载companies
,但始终没有places
。我认为特定搜索没有places
,所以我更改了城市等等,但查询仍然没有返回places
。
所以我尝试单独运行查询,如下所示:
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))))
此查询返回6,600个位置,查询持续30秒。我试图改变&#34; big&#34;中JOIN
s的顺序。查询希望可能导致没有places
被提取,但它没有帮助,仍然没有加载places
。我想知道是什么导致了这个问题。
编辑3:
尝试这样做(省略WHERE
service_areas
表,目的是调试它,并找出原因永远不会被查询返回任何places
:
SELECT places.*,
69.0 * HAVERSINE(places.lat,places.lng, 27.950575,-82.45717) AS distance
FROM places
JOIN companies ON places.company_id = companies.id
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))))
结果是超过5,000家未经过滤的公司,但仍然没有地方。
谢谢
答案 0 :(得分:2)
看来你有两个问题。
places
表格中的内容,并将其与其他表格中的内容相关联。看起来您正在使用(以度为单位)27.950575,-82.45717的特定位置的常量值。如果那是位于美国佛罗里达州坦帕市中心Zack St的一个地方,我猜对了你的常数的含义。我们将这些值称为latpoint
和lonpoint
。
另一个常数3963.19告诉我们你在英里工作。每度有69英里。
要开始解决这个问题,不要淹没在大量的数学中,让我们假设存在一个名为
的存储函数 HAVERSINE(lat1,long1, lat2,long2)
这样的功能可以在这里找到:http://www.plumislandmedia.net/mysql/stored-function-haversine-distance-computation/
通过这种方式,我们可以自由地构建代码,以说服自己拥有正确的代码。
您可以使用一些不错的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
表的许多行,这将节省大量时间。有关更完整的说明,请参阅此处:http://www.plumislandmedia.net/mysql/haversine-mysql-nearest-loc/
现在我们已经准备好了有效查询的框架,我需要做出一个假设。就是这样:你可以这样做。
... places
JOIN companies ON companies.id = places.company_id
因此,将这些内容添加到查询中变得非常容易。 已编辑,其中包含有关companies
和places
表格相关的信息。
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> 。
places
上的复合索引:(company_id, lat, lon)
可能会提高此查询的效果。
如果您使用距离标准以避免混淆,则可能需要省略state_name
条件。