Mysql 地理距离

Mysql 地理距离,mysql,geo,Mysql,Geo,要搜索距离给定位置最近的位置,请按距离排序 我应该使用float还是Point 我应该预先计算cos/sin/sqrt的值吗 我的搜索范围是一个城市内的多个地点 很多老帖子都说mysql没有适当的地理支持,最新版本的mysql也是这样吗 我们使用double来存储纬度和经度。此外,我们预静音(通过触发器)所有在仅查看一个点时可预计算的值。我目前无法访问我们正在使用的公式,稍后将添加此公式。这是为实现最佳速度/精度平衡而优化的 对于定义区域搜索(给我x km内的所有点),我们额外存储lat/ln

要搜索距离给定位置最近的位置,请按距离排序

  • 我应该使用float还是Point
  • 我应该预先计算cos/sin/sqrt的值吗
  • 我的搜索范围是一个城市内的多个地点
  • 很多老帖子都说mysql没有适当的地理支持,最新版本的mysql也是这样吗

  • 我们使用
    double
    来存储
    纬度
    经度
    。此外,我们预静音(通过触发器)所有在仅查看一个点时可预计算的值。我目前无法访问我们正在使用的公式,稍后将添加此公式。这是为实现最佳速度/精度平衡而优化的

    对于定义区域搜索(给我x km内的所有点),我们额外存储lat/lng值乘以
    1e6
    (1000000),因此我们可以通过比较闪电般快速的整数范围限制为平方

    lat BETWEEN 1290000 AND 2344000
    AND
    lng BETWEEN 4900000 AND 4910000
    AND
    distformularesult < 20
    
    搜索中心特定范围内的所有行

    SELECT
              `e`.`id`
            , :earthRadius * ACOS ( :paramGeoLatSinRad * `e`.`geoLatSinRad` + :paramGeoLatCosRad * `m`.`geoLatCosRad` * COS( `e`.`geoLonRad` - :paramGeoLonRad ) ) AS `geoDist`
    
    FROM
              `example` `e`
    WHERE
            `e`.`geoLatInt` BETWEEN :paramMinGeoLatInt AND :paramMaxGeoLatInt
            AND
            `e`.`geoLonInt` BETWEEN :paramMinGeoLonInt AND :paramMaxGeoLonInt
    HAVING `geoDist` < 20
    ORDER BY 
            `geoDist`
    
    示例触发器

    DELIMITER $
    CREATE TRIGGER 'example_before_insert' BEFORE INSERT ON `example` FOR EACH ROW
    BEGIN
        SET NEW.`geoLatInt` := CAST( ROUND( NEW.`geoLat` * 1e6, 0 ) AS SIGNED INTEGER );
        SET NEW.`geoLonInt` := CAST( ROUND( NEW.`geoLon` * 1e6, 0 ) AS SIGNED INTEGER );
        SET NEW.`geoLatSinRad` := SIN( RADIANS( NEW.`geoLat` ) );
        SET NEW.`geoLatCosRad` := COS( RADIANS( NEW.`geoLat` ) );
        SET NEW.`geoLonRad` := RADIANS( NEW.`geoLon` );
    END$
    
    CREATE TRIGGER 'example_before_update' BEFORE UPDATE ON `example` FOR EACH ROW
    BEGIN
        IF NEW.geoLat <> OLD.geoLat OR NEW.geoLon <> OLD.geoLon
        THEN
            SET NEW.`geoLatInt` := CAST( ROUND( NEW.`geoLat` * 1e6, 0 ) AS SIGNED INTEGER );
            SET NEW.`geoLonInt` := CAST( ROUND( NEW.`geoLon` * 1e6, 0 ) AS SIGNED INTEGER );
            SET NEW.`geoLatSinRad` := SIN( RADIANS( NEW.`geoLat` ) );
            SET NEW.`geoLatCosRad` := COS( RADIANS( NEW.`geoLat` ) );
            SET NEW.`geoLonRad` := RADIANS( NEW.`geoLon` );
        END IF;
    END$
    DELIMITER ;
    
    分隔符$
    为每行的“example”在插入之前创建触发器“example\u before\u insert”
    开始
    将NEW.`geoLatInt`:=CAST(将新的.geoLat`*1e6,0)设置为带符号整数);
    将NEW.`geoLon`:=CAST(将新的.geoLon`*1e6,0)设置为带符号整数);
    设置为新。`geoLatSinRad`:=SIN(弧度(新。`geoLat`));
    设置为新。`geoLatCosRad`:=COS(弧度(新。`geoLat`));
    设置为新。`geoLonRad`:=弧度(新。`geoLon`);
    结束$
    为每行在“示例”上更新之前创建触发器“示例更新之前”
    开始
    如果NEW.geoLat OLD.geoLat或NEW.geoLon OLD.geoLon
    然后
    将NEW.`geoLatInt`:=CAST(将新的.geoLat`*1e6,0)设置为带符号整数);
    将NEW.`geoLon`:=CAST(将新的.geoLon`*1e6,0)设置为带符号整数);
    设置为新。`geoLatSinRad`:=SIN(弧度(新。`geoLat`));
    设置为新。`geoLatCosRad`:=COS(弧度(新。`geoLat`));
    设置为新。`geoLonRad`:=弧度(新。`geoLon`);
    如果结束;
    结束$
    定界符;
    

    有问题吗?否则,请尽情享受:)

    添加了配方和用法示例。让我试试这个:)酷,这正是我为我的客户所需要的!;-)
    CREATE TABLE `example` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `geoLat` double NOT NULL DEFAULT '0',
      `geoLon` double NOT NULL DEFAULT '0',
    
      # below is precalculated with a trigger
      `geoLatInt` int(11) NOT NULL DEFAULT '0',
      `geoLonInt` int(11) NOT NULL DEFAULT '0',
      `geoLatSinRad` double NOT NULL DEFAULT '0',
      `geoLatCosRad` double NOT NULL DEFAULT '0',
      `geoLonRad` double NOT NULL DEFAULT '0',
      PRIMARY KEY (`id`),
      KEY `example_cIdx_geo` (`geoLatInt`,`geoLonInt`,`geoLatSinRad`,`geoLatCosRad`,`geoLonRad`)  
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ROW_FORMAT=DYNAMIC
    
    DELIMITER $
    CREATE TRIGGER 'example_before_insert' BEFORE INSERT ON `example` FOR EACH ROW
    BEGIN
        SET NEW.`geoLatInt` := CAST( ROUND( NEW.`geoLat` * 1e6, 0 ) AS SIGNED INTEGER );
        SET NEW.`geoLonInt` := CAST( ROUND( NEW.`geoLon` * 1e6, 0 ) AS SIGNED INTEGER );
        SET NEW.`geoLatSinRad` := SIN( RADIANS( NEW.`geoLat` ) );
        SET NEW.`geoLatCosRad` := COS( RADIANS( NEW.`geoLat` ) );
        SET NEW.`geoLonRad` := RADIANS( NEW.`geoLon` );
    END$
    
    CREATE TRIGGER 'example_before_update' BEFORE UPDATE ON `example` FOR EACH ROW
    BEGIN
        IF NEW.geoLat <> OLD.geoLat OR NEW.geoLon <> OLD.geoLon
        THEN
            SET NEW.`geoLatInt` := CAST( ROUND( NEW.`geoLat` * 1e6, 0 ) AS SIGNED INTEGER );
            SET NEW.`geoLonInt` := CAST( ROUND( NEW.`geoLon` * 1e6, 0 ) AS SIGNED INTEGER );
            SET NEW.`geoLatSinRad` := SIN( RADIANS( NEW.`geoLat` ) );
            SET NEW.`geoLatCosRad` := COS( RADIANS( NEW.`geoLat` ) );
            SET NEW.`geoLonRad` := RADIANS( NEW.`geoLon` );
        END IF;
    END$
    DELIMITER ;