mysql 大 table 具有地理位置 - 查找交叉点
mysql large table with geo-locations - find intersections
我有一个大 table(> 2000 万行)具有这种结构
[ Id, IdUser (int), Latitude(double), Longitude (double), EventDateTime (datetime) ]
我需要找到用户在同一区域(500 米以内)的所有时刻。
最好的解决方案是什么?
首先,我们不必编写充满超越函数的异常复杂 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 ;
现在我们需要比较您 table 中的成对项目以找出巧合。假设我们希望在一分钟内解决时间比较问题。此查询可以解决问题,但需要一段时间。
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 度。
此查询将生成南北距离在 500 米以内的成对观测值,然后使用 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)
上使用复合覆盖索引来尝试优化此查询。
我有一个大 table(> 2000 万行)具有这种结构
[ Id, IdUser (int), Latitude(double), Longitude (double), EventDateTime (datetime) ]
我需要找到用户在同一区域(500 米以内)的所有时刻。
最好的解决方案是什么?
首先,我们不必编写充满超越函数的异常复杂 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 ;
现在我们需要比较您 table 中的成对项目以找出巧合。假设我们希望在一分钟内解决时间比较问题。此查询可以解决问题,但需要一段时间。
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 度。
此查询将生成南北距离在 500 米以内的成对观测值,然后使用 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)
上使用复合覆盖索引来尝试优化此查询。