Mysql
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Mysql

Khoảng cách địa lý MySQL

Chúng tôi đang sử dụng double để lưu trữ latitudelongitude . Ngoài ra, chúng tôi báo trước (bằng cách kích hoạt) tất cả các giá trị có thể tính toán trước khi chỉ nhìn vào một điểm. Tôi hiện không có quyền truy cập vào công thức chúng tôi đang sử dụng, sẽ thêm điều này sau. Điều này được tối ưu hóa để cân bằng tốc độ / độ chính xác tối ưu.

Đối với các tìm kiếm khu vực đã xác định (cho tôi tất cả các điểm trong phạm vi x km), chúng tôi cũng lưu trữ giá trị vĩ độ / lng nhân với 1e6 (1.000.000) để chúng tôi có thể giới hạn thành một hình vuông bằng cách so sánh các phạm vi số nguyên nhanh như chớp, ví dụ:

lat BETWEEN 1290000 AND 2344000
AND
lng BETWEEN 4900000 AND 4910000
AND
distformularesult < 20

CHỈNH SỬA:

Đây là công thức và tính toán trước các giá trị của vị trí hiện tại trong PHP.

WindowSize là một giá trị mà bạn phải tuân theo, đó là hệ số độ 1e6, được sử dụng để thu hẹp các kết quả có thể có trong một hình vuông xung quanh trung tâm, tăng tốc độ tìm kiếm kết quả - đừng quên điều này phải là ít nhất kích thước bán kính tìm kiếm của bạn.

$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;

Tìm kiếm tất cả các hàng trong một phạm vi cụ thể của trung tâm của tôi

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`

Công thức có độ chính xác khá tốt (dưới một mét, tùy thuộc vào vị trí của bạn và khoảng cách giữa các điểm)

Tôi đã tính toán trước các giá trị sau trong bảng cơ sở dữ liệu của mình 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

Trình kích hoạt mẫu

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 ;

Câu hỏi? Nếu không, hãy vui vẻ :)




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Mysql / Php - Ngày và giờ hiện tại

  2. PHPExcel Hiển thị các thẻ html trong hàng bảng mysql

  3. Docker MYSQL '[2002] Kết nối bị từ chối'

  4. PHP SQL:Cách lưu dữ liệu vào nhiều cơ sở dữ liệu từ một dạng html HOẶC cách sao chép dữ liệu từ cơ sở dữ liệu này sang cơ sở dữ liệu khác một cách tự động

  5. MySQL - Có thể sử dụng LIKE trên tất cả các cột trong bảng không?