【问题标题】:Getting only one row for a given column value仅获取给定列值的一行
【发布时间】:2014-04-15 20:41:16
【问题描述】:

我的查询的基本结构是这样的:

  • 我有一个包含个人资料信息的 profiles
  • 我有一个带有位置坐标的locations
  • 我有一个 location_assignment 表,其中只包含 (profile_id, location_id) 对

每个配置文件都分配到一个或多个位置,我要做的是搜索配置文件,然后按与位置坐标的距离顺序返回它们。我的查询是(仅包括相关部分)如下:

SELECT *, 
      (3959*acos(cos(radians(30.292424))*cos(radians(lat))*cos(radians(lng)-  
       radians(-97.73856))+sin(radians(30.292424))*sin(radians(lat)))) AS distance,
      `profiles`.`name` as profilename, 
      `profiles`.`profile_id` as profile_id
 FROM (`profiles`)
 JOIN `location_assignment` 
          ON `profiles`.`profile_id` =`location_assignment`.`profile_id`
 JOIN `locations` 
          ON `location_assignment`.`location_id` = `locations`.`location_id`
HAVING `distance` < 50
ORDER BY `distance`
LIMIT 3"

(选择行中的那个粗略的东西将locations 表中的纬度/经度字段转换为与给定输入纬度/经度的距离)

但是,我的查询使配置文件在结果中出现多次,每次分配给他的位置一次。我希望每个配置文件只出现一次,并带有最短距离的位置信息。

我的下意识反应是使用group_by location_id,但我想确保获得与输入坐标距离最小的位置。

【问题讨论】:

  • 你的代码中有一个HAVING,所以要么你缺少GROUP BY,要么你想要一个WHERE
  • 您可能希望在您的选择中指定连接类型(这些应该是 LEFT JOIN)
  • @Rafa:抱歉,我删除了一堆不相关的其他表连接(此查询跨越七个表),是的,我删除的其中一个是 WHERE。完整的子句是“WHERE procedures.procedure_id = 18 AND base_price > 0 HAVING distance distance”

标签: mysql sql geolocation proximity


【解决方案1】:

去长角牛!

让我们从在位置表中找到正确的行开始。

SELECT DISTINCT location_id
  FROM locations
 ORDER BY your_spherical_cosine_law_distance_formula
 LIMIT 1

这将为您提供唯一的位置 ID。

现在您想将其用作子查询来获取适当的配置文件行。你这样做:

 SELECT whatever
   FROM (
        SELECT DISTINCT location_id
          FROM locations
         ORDER BY your_spherical_cosine_law_distance_formula
         LIMIT 1
        ) AS one
   JOIN location_assignment AS la ON one.location_id = la.location_id
   JOIN profiles AS p on p.profile_id =la.profile_id

这应该会为您提供适当的配置文件行列表,而不会重复。

你没有问这个问题,但我希望你没有太多的位置行。您正在使用的查询必然会扫描整个表并对每一行进行大量数学运算。您的 HAVING 子句确实没有帮助。为了加快速度,您需要将距离搜索与边界矩形搜索结合起来。这可能会有所帮助。 http://www.plumislandmedia.net/mysql/haversine-mysql-nearest-loc/

【讨论】:

  • 这看起来可行,非常感谢优化链接!目前事情很小,但我敢打赌这会在不久的将来咬我。
  • 还偷偷看我的坐标 ;)
  • 我在将这种优化与 only-having-profiles-show-up-once 合成时遇到了一些麻烦,因为执行上面的 ORDER BY 最终会计算到每个位置的距离。使用链接中提供的子查询,我可以按距离顺序获取位置,并可以将它们与配置文件连接起来,但我似乎无法合并“仅使用一次配置文件,位置在最短距离”的逻辑。我可以GROUP BY profile_id,但我不一定得到正确的location_id
  • 您的要求或架构一定有一些我不明白的地方。你想要一个最接近的locations 行,对吧? LIMIT 1?它如何变成多个profiles 行?
  • 我想要一个给定点 50 英里范围内的每个配置文件的列表,按距离排序。问题是配置文件可能与多个位置(每个配置文件的多个位置)相关联,我只希望一个配置文件显示一次,与最接近搜索源的位置相关联。我当前的查询是pastebin.com/Ph1A46Mx,但如果配置文件与多个位置相关联,它会多次显示配置文件。添加GROUP BY profiles.profile_id 不一定会产生最接近的location 行(带有地址等)
【解决方案2】:

我认为您应该将MIN() 函数添加到距离计算中,以获取每个配置文件到最近位置的距离。此外,将GROUP BY 添加到按个人资料信息分组。

(我知道 MySQL 允许返回不在 GROUP BY 中的列,但我不建议这样做,所以我从您的 SELECT 中删除了 *

SELECT MIN(3959*acos(cos(radians(30.292424))*cos(radians(lat))*cos(radians(lng)-  
       radians(-97.73856))+sin(radians(30.292424))*sin(radians(lat)))) AS distance,
      `profiles`.`name` as profilename, 
      `profiles`.`profile_id` as profile_id
 FROM (`profiles`)
 JOIN `location_assignment` 
          ON `profiles`.`profile_id` =`location_assignment`.`profile_id`
 JOIN `locations` 
          ON `location_assignment`.`location_id` = `locations`.`location_id`
GROUP BY `profiles`.`name`, `profiles`.`profile_id`
HAVING `distance` < 50
ORDER BY `distance`
LIMIT 3"

【讨论】:

  • 我相信这不会给我与最小距离的位置相关的locations 字段(位置表有我需要提取的各种其他信息,并且需要关联到远方)
  • 好的,如果你这样做了,Ollie Jones 的回答对你来说会更好。
猜你喜欢
  • 2014-10-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-05
相关资源
最近更新 更多