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

Cách tiếp cận nào nhanh hơn để nhận tất cả POI từ MySQL / MariaDB với PHP / Laravel

Bạn sử dụng công thức nào cho khoảng cách không quan trọng lắm. Điều quan trọng hơn là số hàng bạn phải đọc, xử lý và sắp xếp. Trong trường hợp tốt nhất, bạn có thể sử dụng chỉ mục cho một điều kiện trong mệnh đề WHERE để giới hạn số hàng được xử lý. Bạn có thể cố gắng phân loại các vị trí của mình - Nhưng nó phụ thuộc vào bản chất dữ liệu của bạn, nếu điều đó hoạt động tốt. Bạn cũng sẽ cần phải tìm ra "danh mục" để sử dụng. Một giải pháp chung hơn sẽ là sử dụng CHỈ SỐ SPATIAL ST_Within () chức năng.

Bây giờ chúng ta hãy chạy một số thử nghiệm ..

Trong DB của tôi (MySQL 5.7.18), tôi có bảng sau:

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

Dữ liệu đến từ Cơ sở dữ liệu các thành phố miễn phí trên thế giới và chứa 3173958 (3,1 triệu) hàng.

Lưu ý rằng geoPoint là dư thừa và bằng POINT(longitude, latitude) .

Kết luận người dùng ở đâu đó ở Luân Đôn

set @lon = 0.0;
set @lat = 51.5;

và bạn muốn tìm vị trí gần nhất từ ​​các cities bảng.

Một truy vấn "tầm thường" sẽ là

select c.cityId, c.accentCity, st_distance_sphere(c.geoPoint, point(@lon, @lat)) as dist
from cities c
order by dist
limit 1

Kết quả là

988204 Blackwall 1085.8212159861014

Thời gian thực thi:~ 4.970 giây

Nếu bạn sử dụng hàm ít phức tạp hơn ST_Distance() , bạn sẽ nhận được cùng một kết quả với thời gian thực hiện là ~ 4,580 giây - không chênh lệch quá nhiều.

Lưu ý rằng bạn không cần phải lưu trữ một điểm địa lý trong bảng. Bạn có thể sử dụng tốt (point(c.longitude, c.latitude) thay vì c.geoPoint . Tôi ngạc nhiên là nó thậm chí còn nhanh hơn (~ 3,6 giây cho ST_Distance và ~ 4.0 giây cho ST_Distance_Sphere ). Nó có thể còn nhanh hơn nếu tôi không có geoPoint cột nào cả. Nhưng điều đó vẫn không quan trọng lắm, vì bạn không muốn người dùng chờ đợi, vì vậy hãy ghi lại thời gian nghỉ ngơi, nếu bạn có thể làm tốt hơn.

Bây giờ, hãy xem cách chúng ta có thể sử dụng SPATIAL INDEX với ST_Within() .

Bạn cần xác định một đa giác sẽ chứa vị trí gần nhất. Một cách đơn giản là sử dụng ST_Buffer () điều này sẽ tạo ra một đa giác có 32 điểm và gần là một hình tròn *.

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

Kết quả là như nhau. Thời gian thực hiện là ~ 0.000 giây (đó là những gì khách hàng của tôi ( HeidiSQL ) nói).

* Lưu ý rằng @radius được ký hiệu bằng độ và do đó đa giác sẽ giống hình elip hơn là hình tròn. Nhưng trong các bài kiểm tra của tôi, tôi luôn nhận được kết quả tương tự như với giải pháp đơn giản và chậm chạp. Mặc dù vậy, tôi sẽ điều tra nhiều trường hợp phức tạp hơn, trước khi sử dụng nó trong mã sản xuất của mình.

Bây giờ bạn cần tìm bán kính tối ưu cho ứng dụng / dữ liệu của mình. Nếu nó quá nhỏ - bạn có thể không nhận được kết quả hoặc bỏ lỡ điểm gần nhất. Nếu nó quá lớn - bạn có thể cần phải xử lý quá nhiều hàng.

Dưới đây là một số con số cho trường hợp thử nghiệm đã cho:

  • @radius =0,001:Không có kết quả
  • @radius =0,01:chính xác một vị trí (loại may mắn) - Thời gian thực hiện ~ 0,000 giây
  • @radius =0,1:55 vị trí - Thời gian thực thi ~ 0,000 giây
  • @radius =1,0:2183 vị trí - Thời gian thực thi ~ 0,030 giây


  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 SELECT INTO OUTFILE Tùy chọn xuất

  2. Thông báo cho người dùng về sự thay đổi cơ sở dữ liệu? JavaScript / AJAX

  3. Làm cách nào tôi có thể sử dụng YEAR (), MONTH () và DAY () của SQL trong Doctrine2?

  4. Cách gỡ lỗi php / MySQL COUNT (id) trả về 1 thay vì tổng giá trị mục nhập

  5. Đổi tên cơ sở dữ liệu MySQL