Belirli bir yer işaretindeki bir aralıktaki tüm yer işaretlerini nasıl verimli bir şekilde arayabilirim?


14

Belirli bir dönüm noktasının 10 km / milindeki (bu hikaye için önemli değil) tüm yerleri bulan bir coğrafi arama projesiyle başlamaya çalışıyorum.

Diyelim ki 1.000.000 yer işaretinden oluşan bir veritabanım var. Belli koordinatlara sahip bir dönüm noktasının 10 mil aralığındaki tüm yer işaretlerini bulmak için, aramamdan bir yer işareti ile 1.000.000 yer işareti arasındaki bir mesafeyi hesaplamam gerekir.

Bunu yapmanın daha iyi bir yolu var mı?

Alternatif olarak, ülke, bölge, şehir, mahalle, iş, tarihi vb. Şehir, bir bölgenin, bir ülkenin, vb.

Google Haritalar API'sı yardımcı olabilir mi?


5
Muhtemelen hızlı bir Manhattan mesafe hesaplaması yaparak ve daha sonra 10 km'lik bir karenin içindeki ancak 10 km'lik yarıçapın dışındaki yer işaretlerini hariç tutmak için ikinci bir filtre uygulayarak bir çok şeyi ortadan kaldırabilirsiniz.
Neil

3
Hangi veritabanı teknolojisini kullanıyorsunuz? Cevap veritabanı agnostik değildir.
jpmc26

1
@Neil İkinci bir geçiş olarak, x ve y'nin her ikisinin de asıl mesafeyi hesaplamadan menşe 7km'de düştüğü herhangi bir yer işareti ekleyebilirsiniz.
JimmyJames

Yanıtlar:


10

SQL Server 2008'den beri, konumları (enlem / boylam çiftleri) depolayan ve konumla ilgili sorgular yazmanızı kolaylaştıran bir coğrafya veri türü vardır.

Bu konuyu derinlemesine tartışan mevcut bir StackOverflow yanıtı vardır.

En yakın 7 öğeyi bulmak için temel bir sorgu :

USE AdventureWorks2012  
GO  
DECLARE @g geography = 'POINT(-121.626 47.8315)';  
SELECT TOP(7) SpatialLocation.ToString(), City FROM Person.Address  
WHERE SpatialLocation.STDistance(@g) IS NOT NULL  
ORDER BY SpatialLocation.STDistance(@g);  

100m içinde her şeyi bulmak için temel bir sorgu (sorunun ikinci cevabı)

-- Get the center point
DECLARE @g geography
SELECT @g = geo FROM yourTable WHERE PointId = something

-- Get the results, radius 100m
SELECT * FROM yourTable WHERE @g.STDistance(geo) <= 100

11
@KonradRudolph: Büyük bir satır sayısı olan bir tabloda sorgulamak için kullanılan herhangi bir SQL sütunu için olduğu gibi. Haklısınız, ancak bu yorum yanıt olarak gönderilen hemen hemen tüm SQL sorguları için geçerlidir.
Flater

2
Soruda "MS SQL Server" ı nereden okudunuz?
Doc Brown

3
@Daha normalde açık ve gereksiz olacağını kabul ediyorum, ancak OP'nin ifadeleri, bu tür mekanizmalardan habersiz olduklarını gösteriyor gibi görünüyor.
Konrad Rudolph

2
@ jpmc26: Geçerli bir seçenek listelediğime ve başka bir seçenek eklemediğime çok sevindiniz mi? Ne? PostGIS eklemenin uygun olduğunu düşünüyorsanız, yanıtı kendiniz ekleyin (yaptığınız) ve sizinle aynı fikre sahip olmadığı için başkalarını eleştirmeye başvurmayın.
flater

3
Cevabınız bana sadece bir MS SQL satış konuşması olarak geliyor. Veritabanlarını , durumlarının sadece moreso görünmesini sağladığını sorgulamadan 10 bin dolara mal olacak bir şeye değiştirmelerini öneren yorumlarınız . OP'nin aslında sorgularını nasıl uygulayabileceğini veya bunu yapmanın ve uzamsal dizinin esnemesinin kullanıldığını tartışmaması bile MS SQL'de diğer DB'lerde olduğu kadar basit değildir. Altta yatan kavramlardan hiçbirini tartışmaz. "Geçerli" olup olmadığına bakılmaksızın, kötü bir yanıttır. Bu yüzden beni rahatsız ediyor.
jpmc26

29

CBS (coğrafi bilgi sistemleri) sorgularını destekleyen bir veritabanı kullanın . Çoğu veritabanında bu açıkça desteklenir veya uzantıları vardır, ancak ayrıntılar veritabanına özgü olacaktır ( yanıtlarında Flater, SQL sunucusu için sözdizimini gösterir).

Bu tür sorguları uygulamanızda uygulamanız gerekiyorsa, örneğin bir kd Ağacı gibi uzamsal sorgulara izin veren bir veri yapısı uygulayabilirsiniz . Bu, ikili bir arama ağacı gibidir, ancak ağacın her düzeyi farklı bir koordinat boyutunda bölümlenir. Bu, aramayı daha küçük bir dizi uygulanabilir adayla kısıtlamanıza olanak tanır. Etkili bir şekilde, “10km yarıçapı” aramanızı her bir koordinat boyutu için sınırlara dönüştürür ve ağaca girerken sınırları sıkarsınız.



8
PostGIS en önde gelen ücretsiz seçenektir. SQL Server'ın çok temel GIS türleri ve işlevlerinden çok, çok daha fazlasını destekler . Ancak bu temel işlevselliktir.
jpmc26

@ amon jpmc26 adlı kullanıcının yorumunu iyi bir ek olarak görüyorum ve örneğinizi eleştirmek kadar değil. "Sıfırdan başlamak istiyorsanız, lisanslı bir DB için ödeme yapmanız gerekmez - bu ücretsiz, açık kaynak kodlu hileyi de gerçekten iyi yapar".
mgarciaisaia

11

Evet, daha iyi bir yol var. Bir uzamsal dizin kullanmanız gerekir . Bu dizinler, geometrileri çok hızlı bir şekilde filtrelemek için geometrilerle ilgili meta verileri düzenleyerek, açıkladığınız hesaplamalardan kaçınarak çok fazla CPU döngüsünden tasarruf sağlar. Tüm büyük ilişkisel veritabanları bir uzamsal geometri türü ve onlarla birlikte gitmek için dizinler sağladığından, birini kendiniz uygulamaktan rahatsız olmamalısınız.

Bakmak istediğiniz şey "mesafe içinde" sorgulardır (diğer bazı geometrilerin belirli bir mesafesindeki geometriler için sorgular). Bunlar çok standart ve çözülmüş bir sorundur ve yukarıdaki tüm veritabanlarında (ve birkaçında yerleşik olarak) mümkündür:

  • PostGIS: ST_DWithin
  • SQL Server: STDistance(Bu işlevin 3D coğrafya sürümünde dizin kullanımının desteklendiği açık değildir)
  • Oracle: SDO_WITHIN_DISTANCE(Bu açıkça dizin kullanımını tetikleyeceğini söylemez. Sorgu planını iki kez kontrol ederim. Dizini kullanmak için bir uygulamanız gerekebilir SDO_FILTER.)
  • MySQL: Bunu hala anlıyorum.

Dizin kullanımını tetiklemek için geçici çözüm

Sistemin bu sorgularla uzamsal dizini kullanmasında sorun yaşadığınız en kötü durumda, ek bir filtre ekleyebilirsiniz. Arama noktanızda ortalanmış 2 * (arama mesafesi) kenarları olan bir kare sınırlayıcı kutu oluşturacak ve gerçek mesafeyi kontrol etmeden önce tablo geometrilerinin sınırlama kutularını bununla karşılaştıracaksınız . ST_DWithinYukarıda PostGIS 'in dahili olarak yaptığı şey budur .


CBS'de mesafe

Mekansal indeksler harika ve probleminize kesinlikle doğru çözüm olsa da, mesafe hesaplaması mantıksal olarak karmaşık olabilir. Özellikle, verilerinizin hangi projeksiyonda (temel olarak koordinat sistemi için tüm parametreler) depolandığı konusunda endişelenmeniz gerekir . Çoğu 2D projeksiyon (çeşitli enlem / uzun projeksiyonlar gibi açısal koordinat sistemleri dışındaki şeyler) uzunluğu önemli ölçüde deforme eder. Örneğin, Web Mercator projeksiyonu (Google, Bing ve diğer tüm büyük temel harita sağlayıcıları tarafından kullanılan projektör) , konum ekvatordan uzaklaştıkça alanları ve mesafeleri giderek genişletir . Resmi olarak CBS'de eğitim almadığım için yanılıyor olabilirim, ancak 2B projeksiyonlar için gördüğüm en iyi şey, bir mesafeden doğru mesafeler vaat eden bazı spesifik olanlartüm dünyada tek, sabit bir nokta . (Hayır, her sorgu için farklı bir projeksiyon kullanmak pratik değildir; dizinlerinizi işe yaramaz hale getirir.)

Sonuç olarak, matematiğinizin doğru olduğundan emin olmanız gerekir. Bunu bir geliştirme perspektifinden yapmanın en basit yolu, açısal projeksiyonlar kullanmak (bunlar genellikle "coğrafi" olarak adlandırılır) ve bir küremsi model kullanarak matematik yapmayı destekleyen işlevleri kullanmaktır, ancak bu hesaplamalar 2B meslektaşlarından biraz daha pahalıdır. ve bazı DB'ler bunları dizine eklemeyi desteklemeyebilir. Ancak, bunları kullanarak kabul edilebilir bir performans elde edebiliyorsanız, muhtemelen bu yol. Diğer bir yaygın seçenek ise, verileriniz dünyanın belirli bir bölgesi ile sınırlıysa hem mesafeleri hem de alanları düzeltmeye oldukça yaklaştıran bölgesel projeksiyonlardır (UTM bölgeleri gibi). Uygulamanız için en iyi olan şey özel gereksinimlerinize bağlı olacaktır,

Bu, yerleşik uzamsal dizinler kullanmasanız bile geçerlidir. Verileriniz, şu anda hangi teknolojiyi veya tekniği kullandığınızdan veya gelecekte kullanmanızdan bağımsız olarak bazı projeksiyonlara sahiptir ve şu anda yaptığınız sorguları ve hesaplamaları zaten etkilemektedir.


3

Mümkünse bir veritabanında belirli destek kullanmanın bunu yapmanın en mantıklı yolu olacağını kabul ediyorum.

Ancak bunu belirli bir desteği olmayan bir veritabanında yapmak zorunda kalsaydım, örneğin (y> (y1 - rad)) AND (y <(y1 + rad)) AND (x> ( x1 - rad)) AND (x <(x1 + rad)). Puanlarınızın bir kare için kabaca eşitleme sorgulaması olduğunu varsayarsak, gerçek eşleşmelerinizi ve yaklaşık% 30 ekstra yanlış eşleşmeleri elde edersiniz. Daha sonra yanlış eşleşmeleri kaldırabilirsiniz.


Ancak uygun bir uzamsal dizin olmadan, bu tür bir sorgu tüm veritabanının en kötüsünde, en iyisi, endeksinize bağlı olarak verilen enlem VEYA boylam aralığındaki tüm öğeleri, yani bir kare yerine bir "bandı" tarar. Performansı öldürmek istemiyorsanız, uzamsal dizinleri destekleyen bir veritabanı kullanın!
jcaron

İnanıyorum @jcaron bu sorgu sıradan B-tree üzerinde indeksi ile optimize edilebilecek xve y. (Belki de birleştirilmiş, belki de ayrı. Pratikte hangisinin daha iyi çalıştığını anlamak için biraz profil yaparım.)
jpmc26

@ jpmc26 Hayır, yapamaz. Bunu düşün, göreceksin.
jcaron

@jcaron Belki de açıkça basit olmayan bir şey hakkında şifreli olmasaydınız daha iyi olurdu. B-ağaçları BETWEENsorgular için kullanılabilir . Neden en kötü durumda 2 dizin olamazdı ve sonra her dizin filtrelenen sonuçları bir araya katılmak görmüyorum. (Bu, RDBMS'lerin birden çok dizin kullanmaya değer bulduklarında dahili olarak yaptıkları bir şeydir.) Birleştirilmiş bir dizin çalışırsa, bir boyutu tamamen ilk düzeyde filtrelemeli ve sonra ikinci düzeyde nispeten hızlı bir şekilde daraltmalıdır.
jpmc26

2
@jcaron aslında indeks gibi bir şey için kullanabilirsiniz y between -68 and -69 and x between 10 and 11ama elbette mekansal dizin bu görev için daha iyi bir iş yapmak
Juan Carlos Oropeza
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.