Saat ini saya memiliki kurang dari satu juta lokasi dalam database mysql semuanya dengan informasi garis bujur dan garis lintang.
Saya mencoba menemukan jarak antara satu titik dan banyak titik lainnya melalui kueri. Ini tidak secepat yang saya inginkan terutama dengan 100+ hit per detik.
Apakah ada permintaan yang lebih cepat atau mungkin sistem yang lebih cepat selain mysql untuk ini? Saya menggunakan kueri ini:
SELECT
name,
( 3959 * acos( cos( radians(42.290763) ) * cos( radians( locations.lat ) )
* cos( radians(locations.lng) - radians(-71.35368)) + sin(radians(42.290763))
* sin( radians(locations.lat)))) AS distance
FROM locations
WHERE active = 1
HAVING distance < 10
ORDER BY distance;
Catatan: Jarak yang disediakan adalah dalam Mil . Jika Anda membutuhkan Kilometer , gunakan 6371
sebagai ganti 3959
.
Jawaban:
Buat poin Anda menggunakan
Point
nilaiGeometry
tipe data dalamMyISAM
tabel. Pada Mysql 5.7.5,InnoDB
tabel sekarang juga mendukungSPATIAL
indeks.Buat
SPATIAL
indeks pada titik-titik iniGunakan
MBRContains()
untuk menemukan nilai:, atau, di dalam
MySQL 5.1
dan di atas:Ini akan memilih semua titik kira-kira di dalam kotak
(@lat +/- 10 km, @lon +/- 10km)
.Ini sebenarnya bukan kotak, tetapi persegi panjang bulat: garis lintang dan garis bujur terikat bola. Ini mungkin berbeda dari persegi panjang polos di Tanah Franz Joseph , tetapi cukup dekat dengan itu di tempat-tempat yang paling dihuni.
Terapkan penyaringan tambahan untuk memilih semua yang ada di dalam lingkaran (bukan persegi)
Mungkin menerapkan penyaringan halus tambahan untuk memperhitungkan jarak lingkaran besar (untuk jarak besar)
sumber
@lon - 10 / ( 111.1 / cos(@lat))
(dan menjadi yang kedua dalam pasangan setelah semuanya benarcos(lon)
akurat hanya untuk jarak yang lebih kecil. Lihat janmatuschek.de/LatitudeLongitudeBoundingCoordinates111.(1)
km dalam derajat garis lintang.mypoint
adalah bidang dalam tabel yang menyimpan koordinat.Bukan jawaban spesifik MySql, tetapi itu akan meningkatkan kinerja pernyataan sql Anda.
Apa yang Anda lakukan secara efektif adalah menghitung jarak ke setiap titik dalam tabel, untuk melihat apakah jaraknya dalam 10 unit dari titik tertentu.
Apa yang dapat Anda lakukan sebelum Anda menjalankan sql ini, adalah membuat empat poin yang menarik kotak 20 unit di samping, dengan titik Anda di tengah yaitu. (x1, y1). . . (x4, y4), di mana (x1, y1) adalah (diberikan + 10 unit, diberikanLat + 10 unit). . . (diberikan Panjang - 10 unit, diberikanLat -10 unit). Sebenarnya, Anda hanya perlu dua poin, kiri atas dan kanan bawah memanggil mereka (X1, Y1) dan (X2, Y2)
Sekarang pernyataan SQL Anda menggunakan titik-titik ini untuk mengecualikan baris yang pasti lebih dari 10u dari titik yang Anda berikan, dapat menggunakan indeks pada garis lintang & bujur, sehingga akan menjadi urutan besarnya lebih cepat dari apa yang Anda miliki saat ini.
misalnya
Pendekatan kotak dapat mengembalikan positif palsu (Anda dapat mengambil poin di sudut-sudut kotak yang> 10u dari titik yang diberikan), jadi Anda masih perlu menghitung jarak setiap titik. Namun ini lagi akan jauh lebih cepat karena Anda secara drastis membatasi jumlah poin untuk menguji poin dalam kotak.
Saya menyebut teknik ini "Berpikir di dalam kotak" :)
EDIT: Bisakah ini dimasukkan ke dalam satu pernyataan SQL?
Saya tidak tahu apa yang bisa dilakukan mySql atau Php, maaf. Saya tidak tahu di mana tempat terbaik adalah untuk membangun empat poin, atau bagaimana mereka dapat diteruskan ke permintaan mySql di Php. Namun, begitu Anda memiliki empat poin, tidak ada yang menghentikan Anda menggabungkan pernyataan SQL Anda sendiri dengan milik saya.
Saya tahu dengan MS SQL saya dapat membangun pernyataan SQL yang menyatakan empat float (X1, Y1, X2, Y2) dan menghitungnya sebelum pernyataan pilih "utama", seperti saya katakan, saya tidak tahu apakah ini dapat dilakukan dengan MySql. Namun saya masih cenderung untuk membangun empat poin dalam C # dan meneruskannya sebagai parameter ke query SQL.
Maaf saya tidak bisa membantu, jika ada yang bisa menjawab bagian spesifik MySQL & Php ini, silakan edit jawaban ini untuk melakukannya.
sumber
Fungsi MySQL berikut diposting di posting blog ini . Saya belum banyak mengujinya, tetapi dari apa yang saya kumpulkan dari pos, jika bidang lintang dan bujur Anda diindeks , ini mungkin cocok untuk Anda:
Penggunaan sampel:
Mengasumsikan tabel yang disebut
places
dengan bidanglatitude
&longitude
:sumber
SELECT ROUND(((ACOS(SIN(lat1 * PI() / 180) * SIN(lat2 * PI() / 180) + COS(lat1 * PI() / 180) * COS(lat2 * PI() / 180) * COS((lnt1 - lnt2) * PI() / 180)) * 180 / PI()) * 60 * 1.1515) * 1.609344 * 1000) AS distance
Saya perlu memecahkan masalah yang sama (menyaring baris berdasarkan jarak dari satu titik) dan dengan menggabungkan pertanyaan asli dengan jawaban dan komentar, saya datang dengan solusi yang sangat cocok untuk saya di MySQL 5.6 dan 5.7.
coordinates
adalah bidang dengan tipePOINT
dan memilikiSPATIAL
indeks6371
untuk menghitung jarak dalam kilometer56.946285
adalah garis lintang untuk titik pusat24.105078
adalah garis bujur untuk titik pusat15
adalah jarak maksimum dalam kilometerDalam pengujian saya, MySQL menggunakan indeks SPATIAL di
coordinates
lapangan untuk dengan cepat memilih semua baris yang ada dalam persegi panjang dan kemudian menghitung jarak aktual untuk semua tempat yang disaring untuk mengecualikan tempat dari sudut persegi panjang dan hanya menyisakan tempat di dalam lingkaran.Ini adalah visualisasi hasil saya:
Bintang abu-abu memvisualisasikan semua titik di peta, bintang kuning adalah yang dikembalikan oleh permintaan MySQL. Bintang abu-abu di dalam sudut persegi panjang (tetapi lingkaran luar) dipilih oleh
MBRContains()
dan kemudian tidak dipilih olehHAVING
klausa.sumber
jika Anda menggunakan MySQL 5.7. *, maka Anda dapat menggunakan st_distance_sphere (POINT, POINT) .
sumber
Ini adalah kueri perhitungan jarak antara ke titik di MySQL, saya telah menggunakannya dalam database yang panjang, itu berfungsi dengan sempurna! Catatan: lakukan perubahan (nama database, nama tabel, kolom dll) sesuai kebutuhan Anda.
sumber
sumber
sumber
sumber
Fungsi MySQL yang mengembalikan jumlah meter antara dua koordinat:
Untuk mengembalikan nilai dalam format yang berbeda, ganti
6371000
fungsi tersebut dengan jari-jari Earth pada unit pilihan Anda. Misalnya, kilometer akan menjadi6371
dan mil akan3959
.Untuk menggunakan fungsi ini, panggil saja seperti yang Anda lakukan pada fungsi lain di MySQL. Misalnya, jika Anda memiliki meja
city
, Anda dapat menemukan jarak antara setiap kota ke setiap kota lain:sumber
Kode lengkap dengan detail tentang cara memasang sebagai plugin MySQL ada di sini: https://github.com/lucasepe/lib_mysqludf_haversine
Saya memposting ini tahun lalu sebagai komentar. Karena @TylerCollier yang ramah menyarankan saya untuk mengirim sebagai jawaban, ini dia.
Cara lain adalah dengan menulis fungsi UDF khusus yang mengembalikan jarak haversine dari dua titik. Fungsi ini dapat menerima input:
Jadi kita bisa menulis sesuatu seperti ini:
untuk mengambil semua rekaman dengan jarak kurang dari 40 kilometer. Atau:
untuk mengambil semua rekaman dengan jarak kurang dari 25 kaki.
Fungsi inti adalah:
sumber
Perkiraan cepat, sederhana dan akurat (untuk jarak yang lebih kecil) dapat dilakukan dengan proyeksi bola . Setidaknya dalam algoritme perutean, saya mendapat peningkatan 20% dibandingkan dengan perhitungan yang benar. Dalam kode Java sepertinya:
Tidak yakin tentang MySQL (maaf!).
Pastikan Anda tahu tentang batasan (param ketiga dari assertEquals berarti akurasi dalam kilometer):
sumber
Berikut ini adalah deskripsi yang sangat rinci tentang Geo Distance Search dengan MySQL solusi berdasarkan implementasi dari Haversine Formula ke mysql. Deskripsi solusi lengkap dengan teori, implementasi, dan optimalisasi kinerja lebih lanjut. Meskipun bagian optimasi spasial tidak berfungsi dengan benar dalam kasus saya. http://www.scribd.com/doc/2569355/Geo-Distance-Search-with-MySQL
sumber
Bacalah Geo Distance Search dengan MySQL , solusi berbasis implementasi Haversine Formula ke MySQL. Ini adalah deskripsi solusi lengkap dengan teori, implementasi, dan optimalisasi kinerja lebih lanjut. Meskipun bagian optimasi spasial tidak berfungsi dengan benar dalam kasus saya.
Saya perhatikan dua kesalahan dalam hal ini:
penggunaan
abs
dalam pernyataan pilih pada hal. Saya hanya dihilangkanabs
dan itu berhasil.fungsi jarak pencarian spasial pada p27 tidak mengkonversi ke radian atau menggandakan bujur dengan
cos(latitude)
, kecuali data spasialnya dimuat dengan ini dalam pertimbangan (tidak dapat mengatakan dari konteks artikel), tetapi contohnya pada hal. 26 menunjukkan bahwa data spasialnyaPOINT
tidak dimuat dengan radian atau derajat.sumber
sumber
Menggunakan mysql
Lihat: https://andrew.hedges.name/experiments/haversine/
Lihat: https://stackoverflow.com/a/24372831/5155484
Lihat: http://www.plumislandmedia.net/mysql/haversine-mysql-nearest-loc/
CATATAN:
LEAST
digunakan untuk menghindari nilai nol sebagai komentar yang disarankan di https://stackoverflow.com/a/24372831/5155484sumber