Mysql 使用地理空间 ST_Contains 选择计数对于多行非常慢

问题描述 投票:0回答:3

我有一个 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 查询会得到相同的结果。

mysql innodb geospatial
3个回答
2
投票

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

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


0
投票

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

0
投票

在 MySQL 对此进行更好优化之前(我使用的是 8.0.36 版本),如果您有许多具有复杂几何形状的位置(例如 100K+ 全球位置和几何形状,例如阿拉斯加的精确边界),我注意到如果您首先获取几何体的信封(最小边界矩形 MBR),并使用它来预先过滤您的位置,然后将它们放入 ST_CONTAINS 中。

ST_Envelope(ST_GEOMFROMTEXT('MULTIPOLYGON((...))))

在将大部分位置输入 ST_CONTAINS() 之前,它会稀疏化它们,这会大大加快速度。无论如何,你希望这将成为底层算法的一部分。

© www.soinside.com 2019 - 2024. All rights reserved.