【问题标题】:Mysql - select records for lat/lng position that users have in their individual distance rangesMysql - 选择用户在其个人距离范围内的 lat/lng 位置的记录
【发布时间】:2019-06-18 12:23:27
【问题描述】:

我需要了解以下场景 (Mysql) 的数据库查询的最佳实践(最佳性能):

http://sqlfiddle.com/#!9/72191ca/1

我有一个带有纬度/经度位置的“起始项目”(点“关键”,蓝点 lat:47.471630, lng: 8.297835 )。在用户表中,有用户(A、B、C 等)及其纬度/经度位置和以公里为单位的个人范围。

我需要查询 users 表以查找在其预定义范围/距离内具有键的 ID。

应优化查询 - 将大约 40,000 个用户与“关键”纬度/经度位置进行比较。

这是我当前使用的查询。性能相当不错,但是否有另一种可以使用索引的解决方案来做到这一点?

DROP TABLE IF EXISTS users;

CREATE TABLE `users` (
  `user_id` char(1) NOT NULL,
  `lat` decimal(8,5) NOT NULL DEFAULT '0.00000',
  `lng` decimal(8,5) DEFAULT '0.00000',
  `user_range_km` decimal(10,1) NOT NULL DEFAULT '1.0',
  PRIMARY KEY (`user_id`),
  KEY `lat` (`lat`,`lng`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;


INSERT INTO `users` (`user_id`, `lat`, `lng`, `user_range_km`) VALUES
('A', '47.46911', '8.29560', '0.4'),
('B', '47.48169', '8.30264', '0.4'),
('C', '47.49261', '8.31598', '2.9');

SELECT h.*, ( 6371 * acos( cos( radians(47.471630) ) * cos(  radians( h.lat ) ) * cos( radians( h.lng ) - radians(8.297835) ) + sin( radians(47.471630) ) * sin( radians( h.lat ) ) ) ) AS distance 
FROM users h 
HAVING distance <= h.user_range_km;

+---------+----------+---------+---------------+------------------+
| user_id | lat      | lng     | user_range_km | distance         |
+---------+----------+---------+---------------+------------------+
| A       | 47.46911 | 8.29560 |           0.4 | 0.32671077638732 |
| C       | 47.49261 | 8.31598 |           2.9 |  2.7021411331883 |
+---------+----------+---------+---------------+------------------+

在我的示例中,A 和 C 的 Key 在其定义的距离内,因此我需要将 A 和 C 排除在查询之外。请参阅 SQL 小提琴

【问题讨论】:

  • 添加了sqlfiddle
  • 这是一个经常被问到的问题 - 无论是在这里还是在其他地方 - 因为很多人希望快速有效地对大量(而不是如此庞大)的地理数据点集合进行排序。一个技巧是排除不适合边界正方形的数据,边数等于“距离”的两倍。
  • 好吧,我知道如何在查询中使用边界框,我们有一个起点,其中有一个范围来查询用户纬度/经度以排除框外的用户。但是我在这里看不到这种情况,因为每个用户都有一个“个人”边界框。或者您的意思是,在查询中将使用边界框 lat/lngs 而不是直接使用它们的 lat/lngs?还是我在这里漏掉了一点?
  • 正如我所说,这是一个被广泛讨论的话题。

标签: mysql latitude-longitude


【解决方案1】:

有 5 种方法可以完成该任务。你的代码就是其中之一,最慢的一个。这是我对它们的讨论:http://mysql.rjweb.org/doc.php/find_nearest_in_mysql

最简单的下一步是使用“边界框”技术。它涉及向WHERE 和两个INDEXes 添加2 个子句。

围绕一个“键”的“多个用户”只是颠倒角色。传统的问题是关于“用户”周围的“多个项目(企业、卡车等)”。围绕“键”构建边界框。

只有 40K 用户,边界框技术可能就足够了。

好的,你有一个额外的皱纹。不过,BB 应该给你一个很好的第一个过滤器。在您的示例中,使用

MAX(user_range_km) -- which is 2.9

作为 BB 的半径。 (或评论中提到的“边界正方形”宽度的一半。)

然后,不是简单地针对 2.9 测试每个“距离”,而是针对 user_range_km 进行测试。

【讨论】:

  • 感谢您的 cmets - 现在这个问题很清楚了。现在可以使用边界框进行查询。
猜你喜欢
  • 1970-01-01
  • 2020-12-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多