Mysql
 sql >> база данни >  >> RDS >> Mysql

Гео разстояние MySQL

Използваме double за съхраняване на latitude и longitude . Освен това ние предварително изчисляваме (чрез тригери) всички стойности, които са предварително изчислени, когато гледаме само в една точка. В момента нямам достъп до формулата, която използваме, ще добавя това по-късно. Това е оптимизирано за оптимален баланс скорост/прецизност.

За търсене на определени зони (дайте ми всички точки в рамките на 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`

Формулярът има доста добра точност (под метър, в зависимост къде се намирате и какво разстояние е между точката)

Предварително изчислих следните стойности в моята таблица на база данни example

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 ;

Въпроси? Иначе се забавлявайте :)




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. SUBDATE() Примери – MySQL

  2. MySQL -- Актуализирайте, ако съществува, вмъкнете с два ключа

  3. Неизвестна грешка в колоната в този COUNT MySQL израз?

  4. странна грешка при четене от csv файл и импортиране на данни в mysql с помощта на php

  5. Стойността '0000-00-00' не може да бъде представена като java.sql.Date