我有一个 mysql 查询来获取某个区域的所有地点计数。如果我只查询一个 id,它会非常快,如果我查询两个或更多 id,那么它会非常慢。
Areas.geometry 和 Places.location 是 SPATIAL 索引。
areas 表中只有 3 行(均具有复杂的几何形状。第 3 行更复杂),stores 表中有 3000 行。如果您想测试,我构建了一个演示 sql 文件以供导入:geospatial-exemple.sql
一些例子:
此查询运行时间为 260 毫秒:
select a.name,
(
SELECT count(*)
FROM places p
WHERE ST_Contains(a.geometry,p.location)
) as places_count
FROM areas a
WHERE a.id in (1)
此查询运行时间为 320 毫秒:
select a.name,
(
SELECT count(*)
FROM places p
WHERE ST_Contains(a.geometry,p.location)
) as places_count
FROM areas a
WHERE a.id in (3)
此查询运行时间为50秒:
select a.name,
(
SELECT count(*)
FROM places p
WHERE ST_Contains(a.geometry,p.location)
) as places_count
FROM areas a
WHERE a.id in (1,3)
我还尝试使用更复杂的 MULTIPOLYGON 对查询中的 Areas.geometry 进行硬编码
此查询运行时间为 380 毫秒:
select a.name,
(
SELECT count(*)
FROM places p
WHERE ST_Contains(ST_GeomFromText("MULTIPOLYGON((...))",
4326,
'axis-order=long-lat'),p.location)
) as places_count
FROM areas a
WHERE a.id in (1,3)
很明显,运行多个查询比只运行一个查询并等待一分钟要快。如果有人知道这是否是 mysql bug 或者是否有其他方法可以做到这一点? 使用 Join 查询会得到相同的结果。
根据John Powells在这里的回答,空间索引有一个未记录的限制:
为了使“包含”和“相交”函数正常工作,以及要使用的索引,您需要让其中一个几何图形为常量。尽管您将在 MySQL 中看到的所有带有 Intersects/Contains 的示例都是以这种方式工作的,但这似乎没有记录在案。
因此,每个区域运行多个查询确实会更快。
如果您有权创建函数,则可以通过在函数中运行子查询来使用解决方法,其中
areas.geometry
现在将充当 ST_Contains()
的常量参数:
CREATE FUNCTION fn_getplacescount(_targetarea GEOMETRY)
RETURNS INT READS SQL DATA
RETURN (SELECT COUNT(*) FROM places p WHERE ST_Contains(_targetarea, p.location));
现在
SELECT a.name, fn_getplacescount(a.geometry) AS places_count
FROM areas a WHERE a.id in (1,3);
类似于单独运行每个区域,并且应该具有与使用两个单独的查询类似的执行时间。
我会尝试将其表示为联接,看看 MySQL 是否运行得更快。不确定 MySQL 是否优化了空间连接,但在我使用的数据库中它会更快。
类似这样的东西(我没有检查语法):
SELECT areas.name, count(*) as places_count
FROM places p JOIN areas a
ON ST_Contains(a.geometry, p.location)
WHERE a.type = "city"
GROUP BY 1;
在 MySQL 对此进行更好优化之前(我使用的是 8.0.36 版本),如果您有许多具有复杂几何形状的位置(例如 100K+ 全球位置和几何形状,例如阿拉斯加的精确边界),我注意到如果您首先获取几何体的信封(最小边界矩形 MBR),并使用它来预先过滤您的位置,然后将它们放入 ST_CONTAINS 中。
ST_Envelope(ST_GEOMFROMTEXT('MULTIPOLYGON((...))))
在将大部分位置输入 ST_CONTAINS() 之前,它会稀疏化它们,这会大大加快速度。无论如何,你希望这将成为底层算法的一部分。