Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/laravel/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用PHP/Laravel从MySQL/MariaDB获取所有poi,哪种方法更快_Mysql_Laravel_Mariadb_Spatial_Point Of Interest - Fatal编程技术网

使用PHP/Laravel从MySQL/MariaDB获取所有poi,哪种方法更快

使用PHP/Laravel从MySQL/MariaDB获取所有poi,哪种方法更快,mysql,laravel,mariadb,spatial,point-of-interest,Mysql,Laravel,Mariadb,Spatial,Point Of Interest,如果我错了,请纠正我 用户在我的网站上创建了三种获取最近家的方法: 要创建一个包含两列(纬度、经度)且两列都是浮动的表,请执行以下操作: 这是: $latitude = 50; $longitude = 60; SELECT * FROM my_table WHERE (latitude <= $latitude+10 AND latitude >= $latitude-10) AND (longitude <= $longitude+10 AND

如果我错了,请纠正我

用户在我的网站上创建了三种获取最近家的方法:

  • 要创建一个包含两列(纬度、经度)且两列都是浮动的表,请执行以下操作:
  • 这是:

    $latitude = 50;
    $longitude = 60;
    
    SELECT * FROM my_table
        WHERE (latitude  <= $latitude+10  AND latitude  >= $latitude-10)
          AND (longitude <= $longitude+10 AND longitude >= $longitude-10)
    
    $latitude=50;
    $longitude=60;
    从my_表中选择*
    其中(纬度=$latitude-10)
    和(经度=经度$10)
    
    例如,这里的10表示1公里

    在这种方法中,我们也可以使用harvesine公式

  • 将这些列(纬度、经度)合并到一个名为point as point type的列中,然后再次逐个搜索每一行

  • 要将多个点(用户创建的房屋坐标)分类为一个国家/地区的一个类别,即城市,如果查询带有$latitude和$latitude以查看最近的房屋,我将检查它们存储在哪个类别中,以便不搜索所有行,而只搜索此查询的部分(坐标)属于

  • 正如我猜想的那样,由于表中每一行的条件,方法1是缓慢的,如果我使用harvesine公式,方法1也是缓慢的

    如果我使用ST_距离,它看起来又慢了,因为它只是有很多计算

    但如果我使用第3种方法,那么检查每个部分的特定点用户似乎比检查所有行更快。我知道如何为每个原点设置点,但我不知道如何创建多个原点位置,因为一个部分可能位于另一个表中

    顺便说一句,InnoDB支持新版本的MySQL和MariaDB空间索引

    我的问题是:

  • 方法1是否真的很慢,或者其他ST_*函数与此方法相同,以逐个检查所有具有此处提到的公式的行?哪一个更快

  • 除了简单的条件外,第二种方法是否能加快速度?我的意思是,当使用点的类型而不是浮点,或者使用ST_*函数而不是自己做的时候,它会做任何改变吗?我想知道算法是否不同

  • 如果第3种方法是这三种方法中速度最快的,我如何对点进行分类以避免搜索表中的所有行

  • 如何使用空间索引使其尽可能快

  • 如果存在任何其他方法,而我没有提到,您能告诉我如何通过在PHP/Laravel中使用MySQL/MariaDB中的坐标获得最近的家吗


  • 谢谢大家

    包围盒和哈弗森

    在简短的
    选择
    中,您使用的是“边界框”方法,即在地图上绘制一个粗略的正方形。然而,它有几个缺陷

    • 50和60大概是以度为单位的;你说10是以公里为单位的。你不能在不转换其中一个的情况下混合它们
    • 经度比纬度短;需要一个
      cos()
      来修复此问题
    有了这些帮助,边界框可以显著地过滤行,然后可选的haversine测试可以绕过测试的范围

    INDEX(latitude)
    INDEX(longitude)
    
    这种方法具有“中等”性能——其中一个索引将与边界框一起使用,从而快速将候选对象限制为全球范围内的东西(或南北)条带。但这可能仍然有很多候选人

    通过过滤掉大部分行,Haversine调用的数量并不太糟糕;不要担心函数的性能

    如果您有一百万个家庭,那么包含5个家庭(加上一些未通过haversine检查的家庭)的最终边界框可能会涉及几千行,因为只使用了两个索引中的一个。这仍然比获取所有一百万行并使用距离函数检查每一行要好得多

    点和空间索引

    切换到
    需要切换到
    空间
    索引。在此模式下,
    ST_Distance_Sphere()
    可代替哈弗森线。(注意:该功能仅在最新版本中存在。)

    通过过滤掉大部分行,调用
    ST_Distance
    ST_Distance\u Sphere
    的次数并不太多;不要担心函数的性能

    SPATIAL
    搜索使用R-树。在你的询问中,我对他们的表现感觉不好

    方法3

    通过从另一个点分类开始,可以增加复杂性。您还需要检查相邻区域以查看附近是否有点。没有更多细节,我无法判断相对表现

    我的方法

    我有一些复杂的代码,可以扩展到任意多个点。因为您的数据集可能足够小,可以缓存在RAM中,所以对您来说可能有些过分

    对于只有一百万个家庭来说,上面的两个索引可能“足够好”,因此您不需要求助于“我的算法”。我的算法只需触摸大约20行就可以得到所需的5行——不管行的总数是多少

    其他注释


    如果同时存储lat/lng和
    ,则表格将变得笨重;如果尝试混合使用边界框和
    ST
    函数,请记住这一点。

    使用哪个公式计算距离并不重要。更重要的是您必须读取、处理和排序的行数。在最好的情况下,您可以在WHERE子句中使用条件索引来限制处理的行数。您可以尝试对您的位置进行分类,但这取决于数据的性质,如果这样做效果好的话。您还需要找出要使用的“类别”。一个更通用的解决方案是使用空间索引和ST_in()函数

    现在让我们运行一些测试

    在my DB(MySQL)中
    CREATE TABLE `cities` (
        `cityId` MEDIUMINT(9) UNSIGNED NOT NULL AUTO_INCREMENT,
        `country` CHAR(2) NOT NULL COLLATE 'utf8mb4_unicode_ci',
        `city` VARCHAR(100) NOT NULL COLLATE 'utf8mb4_unicode_ci',
        `accentCity` VARCHAR(100) NOT NULL COLLATE 'utf8mb4_unicode_ci',
        `region` CHAR(2) NULL DEFAULT NULL COLLATE 'utf8mb4_unicode_ci',
        `population` INT(10) UNSIGNED NULL DEFAULT NULL,
        `latitude` DECIMAL(10,7) NOT NULL,
        `longitude` DECIMAL(10,7) NOT NULL,
        `geoPoint` POINT NOT NULL,
        PRIMARY KEY (`cityId`),
        SPATIAL INDEX `geoPoint` (`geoPoint`)
    ) COLLATE='utf8mb4_unicode_ci' ENGINE=InnoDB
    
    set @lon = 0.0;
    set @lat = 51.5;
    
    select c.cityId, c.accentCity, st_distance_sphere(c.geoPoint, point(@lon, @lat)) as dist
    from cities c
    order by dist
    limit 1
    
    988204 Blackwall 1085.8212159861014
    
    set @point = point(@lon, @lat);
    set @radius = 0.1;
    set @polygon = ST_Buffer(@point, @radius);
    
    select c.cityId, c.accentCity, st_distance_sphere(c.geoPoint, point(@lon, @lat)) as dist
    from cities c
    where st_within(c.geoPoint, @polygon)
    order by dist
    limit 1