Mysql Select 地理空间计数 ST_Contains 对于多行非常慢

Mysql Select count with geospatial ST_Contains is very slow with multiple rows

我有一个 mysql 查询来获取一个区域的所有地点。如果我只查询一个id,那真的很快,如果我查询两个或更多id,那真的很慢。

Areas.geometry 和 Places.location 是 SPATIAL 索引。

区域table只有3行(都有复杂的几何形状。第3行更复杂),商店有3000行。如果你想测试,我建立了一个演示 sql 文件来导入:geospatial-exemple.sql

一些例子:

此查询 运行ning 在 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) 


此查询 运行ning 在 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 进行硬编码

此查询 运行ning 在 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 错误还是有其他方法可以做到这一点? 使用 Join 查询会得到相同的结果。

我会尝试将其表示为连接,看看 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;

根据John Powells answer here,空间索引有一个未记录的限制:

For the Contains and Intersects functions to work properly, and for the index to be used, you need to have one of the geometries be a constant. This doesn't appear to be documented, although all the examples you will see with MySQL with Intersects/Contains work this way.

所以 运行 每个区域一个区域的多个查询确实会更快。

如果您有创建函数的权限,您可以通过 运行 函数中的子查询使用变通方法,其中 areas.geometry 现在将作为 [=13= 的常量参数]:

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);

类似于 运行 每个区域,并且执行时间应该与使用两个单独的查询相似。