mysql 距离小于_地理距离MySQL

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

>我应该使用浮点还是点?

>我应该预先计算cos / sin / sqrt的值

http://www.movable-type.co.uk/scripts/latlong-db.html

>我的搜索是一个城市内的不同地点.

>许多OLD帖子告诉mysql没有正确的地理支持,最新的MySQL版本也是如此吗?

解决方法:

我们使用双倍来存储纬度和经度.此外,我们预先(通过触发器)预读所有在仅查看一个点时可预先计算的值.我目前无法访问我们正在使用的公式,稍后会添加它.这针对最佳速度/精度平衡进行了优化.

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

lat BETWEEN 1290000 AND 2344000

AND

lng BETWEEN 4900000 AND 4910000

AND

distformularesult < 20

编辑:

这是PHP当前位置值的公式和预先计算.

WindowSize是你必须使用的一个值,它的度数系数为1e6,用于缩小中心周围方形的可能结果,加快查找结果 – 不要忘记这至少应该是你的搜索半径大小.

$paramGeoLon = 35.0000 //my center longitude

$paramGeoLat = 12.0000 //my center latitude

$windowSize = 35000;

$geoLatSinRad = sin( deg2rad( $paramGeoLat ) );

$geoLatCosRad = cos( deg2rad( $paramGeoLat ) );

$geoLonRad = deg2rad( $paramGeoLon );

$minGeoLatInt = intval( round( ( $paramGeoLat * 1e6 ), 0 ) ) - $windowSize;

$maxGeoLatInt = intval( round( ( $paramGeoLat * 1e6 ), 0 ) ) + $windowSize;

$minGeoLonInt = intval( round( ( $paramGeoLon * 1e6 ), 0 ) ) - $windowSize;

$maxGeoLonInt = intval( round( ( $paramGeoLon * 1e6 ), 0 ) ) + $windowSize;

搜索我中心特定范围内的所有行

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`

公式具有相当好的准确度(低于一米,取决于您的位置和点之间的距离)

我在数据库表示例中预先计算了以下值

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 ;

有问题吗?否则玩得开心:)

标签:mysql,geo

来源: https://codeday.me/bug/20190529/1180246.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值