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”,那么该公司将在输出中处于第一位),并且作为查询的结果,我试图接收:

  • 在给定半径内有位置的公司
  • 属于公司的地方以及这些地方在指定半径内
这似乎有点超出我的能力范围,我正在尝试将这两个查询合并为一个查询,因为对于两个查询,我必须使用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个结果;当我第二次运行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
    )是什么意思?ypur
    places
    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