带有地理位置的mysql大表 - 查找交叉点

Posted

技术标签:

【中文标题】带有地理位置的mysql大表 - 查找交叉点【英文标题】:mysql large table with geo-locations - find intersections 【发布时间】:2015-01-07 22:46:05 【问题描述】:

我有一个具有这种结构的大表(> 2000 万行)

[ Id, IdUser (int), Latitude(double), Longitude (double), EventDateTime (datetime) ] 

我需要找到用户在同一区域(500 米内)的所有时刻。

什么是最好的解决方案?

【问题讨论】:

使用 postgresql 和 postgis。 postgis 是一个空间数据库扩展器 您能否更具体地说明您认为某个时刻的时间有多近? 你愿意并且能够分享这个数据集吗? 【参考方案1】:

首先,我们不必编写充满超越函数的极其复杂的 SQL 查询,让我们定义一个存储函数 distance(lat1, lon1, lat2, lon2) 来获取两对点之间的距离。

DELIMITER $$
DROP FUNCTION IF EXISTS distance$$

CREATE FUNCTION distance(
        lat1 FLOAT, lon1 FLOAT,
        lat2 FLOAT, lon2 FLOAT
     ) RETURNS FLOAT
    NO SQL DETERMINISTIC
    COMMENT 'Returns the distance in metres on the Earth
             between two known points of latitude and longitude'
BEGIN
    RETURN 111045 * DEGREES(ACOS(
              COS(RADIANS(lat1)) *
              COS(RADIANS(lat2)) *
              COS(RADIANS(lon2) - RADIANS(lon1)) +
              SIN(RADIANS(lat1)) * SIN(RADIANS(lat2))
            ));
END$$

DELIMITER ;

现在我们需要比较表中的成对项目以找出巧合。假设我们想要一分钟的时间比较分辨率。这个查询可以解决问题,但需要一段时间。

 SELECT DISTINCT a.IdUser, b.IdUser, 
                 DATE_FORMAT (a.EventDateTime, '%Y-%m-%d %H:%i:00) AS EventDateTime
   FROM table a
   JOIN table b
          ON a.IdUser < b.IdUser    /* compare different users */
         AND a.EventDateTime >= b.EventDateTime - INTERVAL 1 HOUR
         AND a.EventDateTime <= b.EventDateTime + INTERVAL 1 HOUR
         AND distance(a.Latitude, a.Longitude, b.Latitude, b.Longitude) <= 500.0

这将起作用,给出一对用户的列表以及他们彼此靠近的时间。但不会很快。

您将尝试使用索引。 (EventDateTime, IdUser) 上的索引可能会有所帮助。您可能应该通过添加这样的时间限制来试验此查询...

   WHERE a.EventDateTime >= CURDATE - INTERVAL 2 DAY
     AND a.EventDateTime <  CURDATE - INTERVAL 1 DAY

因此您无需花费数小时来运行查询。

现在,让我们尝试对自连接进行优化传递,以尝试减少 distance 函数的使用,并更好地使用索引。为了做到这一点,我们需要知道每度(南北)纬度有 ~11045m,因此 500m 是 500/111045 度。

此查询将生成南北 500m 范围内的成对观测值,然后使用WHERE 子句进一步消除相距太远的点。这将减少distance 函数的使用。

 SELECT a.IdUser, b.IdUser, 
        DATE_FORMAT (a.EventDateTime, '%Y-%m-%d %H:%i:00) AS EventDateTime
   FROM table a
   JOIN table b
             ON a.IdUser < b.IdUser    /* compare different users */
            AND a.EventDateTime >= b.EventDateTime - INTERVAL 1 HOUR
            AND a.EventDateTime <= b.EventDateTime + INTERVAL 1 HOUR
            AND a.Latitude >= b.Latitude - (500.0/111045.0)
            AND a.Latitude <= b.Latitude + (500.0/111045.0)
  WHERE distance(a.Latitude, a.Longitude, b.Latitude, b.Longitude) <= 500.0

值得尝试在(IdUser, EventDateTime, Latitude, Longitude) 上使用复合覆盖索引来尝试优化此查询。

【讨论】:

感谢您快速而完整的回复,但在此表中,我有超过 200 万行,并且仍然需要很长时间才能完成此查询。即使在 IdUser、EventDateTime、Lat 和 Long 上设置了多列索引

以上是关于带有地理位置的mysql大表 - 查找交叉点的主要内容,如果未能解决你的问题,请参考以下文章

使用地理位置查找最近的城市

hive ip 地理编码(交叉连接半大表)

MySQL - 如何使用地理位置数据加快搜索速度?

使用带有 jquery 的谷歌地图地理定位 api(wifi 查找)

查找超过 100K 个位置之间的距离

对交叉点进行反向地理编码