Mysql
 sql >> Teknologi Basis Data >  >> RDS >> Mysql

Pendekatan mana yang lebih cepat untuk mendapatkan semua POI dari MySQL/MariaDB dengan PHP/Laravel

Rumus mana yang Anda gunakan untuk jarak tidak terlalu menjadi masalah. Yang lebih penting adalah jumlah baris yang harus Anda baca, proses, dan urutkan. Dalam kasus terbaik, Anda dapat menggunakan indeks untuk kondisi dalam klausa WHERE untuk membatasi jumlah baris yang diproses. Anda dapat mencoba untuk mengkategorikan lokasi Anda - Tapi itu tergantung pada sifat data Anda, jika itu akan bekerja dengan baik. Anda juga perlu mencari tahu "kategori" mana yang akan digunakan. Solusi yang lebih umum adalah dengan menggunakan SPATIAL INDEX dan ST_Within() fungsi.

Sekarang mari kita jalankan beberapa tes..

Di DB saya (MySQL 5.7.18) saya memiliki tabel berikut:

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

Data berasal dari Free World Cities Database dan berisi 3173958 (3.1M) baris.

Perhatikan bahwa geoPoint berlebihan dan sama dengan POINT(longitude, latitude) .

Pertimbangkan pengguna berada di suatu tempat di London

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

dan Anda ingin mencari lokasi terdekat dari cities tabel.

Kueri "sepele" adalah

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

Hasilnya adalah

988204 Blackwall 1085.8212159861014

Waktu eksekusi:~ 4,970 detik

Jika Anda menggunakan fungsi yang tidak terlalu rumit ST_Distance() , Anda mendapatkan hasil yang sama dengan waktu eksekusi ~ 4,580 detik - perbedaan yang tidak terlalu jauh.

Perhatikan bahwa Anda tidak perlu menyimpan titik geografis dalam tabel. Anda juga dapat menggunakan (point(c.longitude, c.latitude) bukannya c.geoPoint . Yang mengejutkan saya, ini bahkan lebih cepat (~3,6 detik untuk ST_Distance dan ~4,0 detik untuk ST_Distance_Sphere ). Mungkin akan lebih cepat jika saya tidak memiliki geoPoint kolom sama sekali. Tapi itu tidak terlalu menjadi masalah, karena Anda tidak ingin pengguna menunggu, jadi log untuk istirahat, jika Anda bisa melakukannya dengan lebih baik.

Sekarang mari kita lihat bagaimana kita dapat menggunakan SPATIAL INDEX dengan ST_Within() .

Anda perlu mendefinisikan poligon yang akan berisi lokasi terdekat. Cara sederhana adalah dengan menggunakan ST_Buffer() yang akan menghasilkan poligon dengan 32 titik dan hampir berbentuk lingkaran*.

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

Hasilnya sama. Waktu eksekusi adalah ~ 0,000 detik (itulah yang dilakukan klien saya (HeidiSQL ) mengatakan).

* Perhatikan bahwa @radius dinotasikan dalam derajat dan dengan demikian poligon akan lebih seperti elips daripada lingkaran. Tetapi dalam pengujian saya, saya selalu mendapatkan hasil yang sama dengan solusi sederhana dan lambat. Saya akan menyelidiki lebih banyak kasus Edge, sebelum saya menggunakannya dalam kode produksi saya.

Sekarang Anda perlu menemukan radius optimal untuk aplikasi/data Anda. Jika terlalu kecil - Anda mungkin tidak mendapatkan hasil, atau kehilangan titik terdekat. Jika terlalu besar - Anda mungkin perlu memproses terlalu banyak baris.

Berikut beberapa angka untuk kasus uji yang diberikan:

  • @radius =0,001:Tidak ada hasil
  • @radius =0,01:tepat satu lokasi (beruntung) - Waktu eksekusi ~ 0,000 detik
  • @radius =0,1:55 lokasi - Waktu eksekusi ~ 0,000 detik
  • @radius =1,0:2183 lokasi - Waktu eksekusi ~ 0,030 detik


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Apa pendekatan terbaik untuk membuat kueri dinamis (menggunakan PHP+MySQL)?

  2. Tabel Sementara Tidak Berfungsi di PHPMyAdmin

  3. Tidak dapat terhubung ke server MySQL jarak jauh dengan kesalahan 61

  4. Menyimpan tanggal di mana hanya tahun yang dapat diketahui

  5. GALAT 1067 (42000):Nilai default tidak valid untuk 'created_at'