दूरी के लिए आप कौन सा फॉर्मूला इस्तेमाल करते हैं, यह ज्यादा मायने नहीं रखता। जो अधिक मायने रखता है वह पंक्तियों की संख्या है जिसे आपको पढ़ना, संसाधित करना और क्रमबद्ध करना है। सबसे अच्छी स्थिति में आप संसाधित पंक्तियों की संख्या को सीमित करने के लिए WHERE क्लॉज में किसी शर्त के लिए एक इंडेक्स का उपयोग कर सकते हैं। आप अपने स्थानों को वर्गीकृत करने का प्रयास कर सकते हैं - लेकिन यह आपके डेटा की प्रकृति पर निर्भर करता है, यदि वह अच्छी तरह से काम करने वाला है। आपको यह भी पता लगाना होगा कि किस "श्रेणी" का उपयोग करना है। एक अधिक सामान्य समाधान यह होगा कि स्थानिक सूचकांक . का उपयोग किया जाए और ST_within() समारोह।
आइए अब कुछ परीक्षण चलाते हैं..
मेरे डीबी (MySQL 5.7.18) में मेरे पास निम्न तालिका है:
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
डेटा फ्री वर्ल्ड सिटीज डेटाबेस से आता है। और इसमें 3173958 (3.1M) पंक्तियाँ हैं।
ध्यान दें कि geoPoint
बेमानी है और POINT(longitude, latitude)
. के बराबर है ।
मान लें कि उपयोगकर्ता लंदन में कहीं स्थित है
set @lon = 0.0;
set @lat = 51.5;
और आप cities
. से निकटतम स्थान खोजना चाहते हैं टेबल।
एक "तुच्छ" प्रश्न होगा
select c.cityId, c.accentCity, st_distance_sphere(c.geoPoint, point(@lon, @lat)) as dist
from cities c
order by dist
limit 1
परिणाम है
988204 Blackwall 1085.8212159861014
निष्पादन समय:~ 4.970 सेकंड
यदि आप कम जटिल फ़ंक्शन का उपयोग करते हैं ST_Distance()
, आपको ~ 4.580 सेकंड के निष्पादन समय के साथ वही परिणाम मिलता है - जो इतना अंतर नहीं है।
ध्यान दें कि आपको तालिका में भू-बिंदु संग्रहीत करने की आवश्यकता नहीं है। आप (point(c.longitude, c.latitude)
. का अच्छा उपयोग कर सकते हैं c.geoPoint
. के बजाय . मेरे आश्चर्य के लिए यह और भी तेज़ है (~ 3.6 सेकंड के लिए ST_Distance
और ST_Distance_Sphere
. के लिए ~4.0 सेकंड ) अगर मेरे पास geoPoint
. न होता तो यह और भी तेज़ हो सकता था स्तंभ बिल्कुल। लेकिन यह अभी भी ज्यादा मायने नहीं रखता है, क्योंकि आप नहीं चाहते कि उपयोगकर्ता प्रतीक्षा करे, इसलिए यदि आप बेहतर कर सकते हैं, तो प्रतिक्रिया के लिए लॉग इन करें।
अब देखते हैं कि हम स्थानिक सूचकांक . का उपयोग कैसे कर सकते हैं ST_Within()
. के साथ ।
आपको एक बहुभुज . परिभाषित करने की आवश्यकता है जिसमें निकटतम स्थान होगा। ST_Buffer() . का उपयोग करने का एक आसान तरीका है जो 32 बिंदुओं के साथ एक बहुभुज उत्पन्न करेगा और लगभग एक वृत्त* होगा।
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
नतीजा वही है। निष्पादन समय ~ 0.000 सेकंड है (यही मेरा क्लाइंट है (HeidiSQL ) कहते हैं)।
* ध्यान दें कि @radius
डिग्री में अंकित है और इस प्रकार बहुभुज एक वृत्त के बजाय एक दीर्घवृत्त की तरह अधिक होगा। लेकिन मेरे परीक्षणों में मुझे हमेशा वही परिणाम मिला जो सरल और धीमे समाधान के साथ था। हालांकि मैं अपने उत्पादन कोड में इसका उपयोग करने से पहले, अधिक बढ़त के मामलों की जांच करूंगा।
अब आपको अपने आवेदन/डेटा के लिए इष्टतम त्रिज्या खोजने की जरूरत है। यदि यह बहुत छोटा है - हो सकता है कि आपको कोई परिणाम न मिले, या निकटतम बिंदु चूक जाए। यदि यह बहुत बड़ा है - तो आपको बहुत अधिक पंक्तियों को संसाधित करने की आवश्यकता हो सकती है।
दिए गए टेस्ट केस के लिए यहां कुछ नंबर दिए गए हैं:
- @radius =0.001:कोई नतीजा नहीं
- @radius =0.01:बिल्कुल एक स्थान (एक प्रकार का भाग्यशाली) - निष्पादन समय ~ 0.000 सेकंड
- @radius =0.1:55 स्थान - निष्पादन समय ~ 0.000 सेकंड
- @radius =1.0:2183 स्थान - निष्पादन समय ~ 0.030 सेकंड