Как вернуть все экземпляры в радиусе ## мили от списка почтовых индексов - PullRequest
4 голосов
/ 13 октября 2010

SQL Server 2008 добавил несколько интересных новых Spacial-типов и дал разработчикам SQL намного более мощные способы манипулирования пространственными данными, но это все еще не позволяет мне эффективно возвращать, например, только те местоположения, которые находятся в радиусе ## мили.длинный список почтовых индексов (от 20 до 15 тысяч различных почтовых индексов).

Есть ли простой способ сделать это?Единственное возможное решение, которое приходит на ум, кажется несколько пугающим из-за созданного хрящевого произведения и, следовательно, невероятного количества вычислений ...

Я искусен в создании CLR SP и функций, если это помогает (как яПредположим, что это будет ...).

Меня не интересует, как найти расстояние между 2 точками (или типами географии), скорее, "это заданное местоположение в пределах ## миль от любого изпочтовые индексы (географические точки) в предоставленном списке? "Сложной частью здесь является список почтовых индексов для поиска.

Спасибо.

Ответы [ 6 ]

4 голосов
/ 12 января 2011

Мне пришлось выполнить поиск геолокации, и после долгих исследований я остановился на использовании географии sql2008.Вам нужна таблица почтовых индексов с лат / долг.Таблица должна выглядеть примерно так:

CREATE TABLE [dbo].[PostalCodes](
    [ID] [bigint] IDENTITY(1,1) NOT NULL,
    [StateID] [bigint] NOT NULL,
    [PostalCode] [varchar](10) NOT NULL,
    [Latitude] [decimal](16, 12) NULL,
    [Longitude] [decimal](16, 12) NULL,
    [GeographyLocation] [geography] NULL,
    [CreatedOn] [datetime] NOT NULL,
    [LastUpdated] [datetime] NOT NULL,
    [GeographyLocation_temp] [varchar](100) NULL,
 CONSTRAINT [PK_PostalCode] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

Я загрузил международный список почтовых индексов из GeoNames.org и импортировал его как tmp_GeoNames.Затем я запустил следующий скрипт, чтобы вставить данные в мою таблицу zipcode и создать пространственный индекс.(Мне пришлось добавить свой собственный столбец StateID и заполнить его, но вы можете пропустить эту часть и удалить ее из скрипта.)

INSERT INTO PostalCodes
(StateID, PostalCode, Latitude, Longitude)
SELECT DISTINCT StateID, PostalCode, Latitude, Longitude FROM temp_GeoNames where stateID is not null

UPDATE PostalCodes
SET GeographyLocation_temp= 'POINT(' + CONVERT(VARCHAR(100),longitude)
+' ' +  CONVERT(VARCHAR(100),latitude) +')'

UPDATE PostalCodes
SET GeographyLocation  =  geography::STGeomFromText(GeographyLocation_temp,4326)

CREATE SPATIAL INDEX  SIndx_SpatialTable_geography_col1
   ON PostalCodes(GeographyLocation);

Наконец я создал функцию, которая принимает lat / long и возвращает всепочтовые индексы в определенном диапазоне.Поскольку он использует пространственный индекс, он очень быстрый.

CREATE FUNCTION [dbo].[PostalCode_SelectNearest]
(
    @Latitude [decimal](16, 12)
    ,@Longitude [decimal](16, 12)
    ,@RangeInMiles int
)
RETURNS @PostalCodes Table (PostalCode varchar(10) PRIMARY KEY NOT NULL, DistanceInMiles FLOAT NULL)
AS
BEGIN
    --Create geography point based on Lat/Long passed ... careful, the values passed are reversed from normal thinking
    DECLARE @g geography;
    SET @g = geography::STGeomFromText('POINT(' +
                                         CONVERT(varchar,@Longitude) + ' ' +
                                         CONVERT(varchar,@Latitude) + ')', 4326);
    --Select the nearest Postal Codes
    INSERT INTO @PostalCodes (PostalCode, DistanceInMiles)
    SELECT PostalCode, GeographyLocation.STDistance(@g)/1609.344 as DistanceInMiles
    FROM PostalCodes
    WHERE GeographyLocation.STDistance(@g)<=(@RangeInMiles * 1609.344)

    RETURN;
END

Я понимаю, что это не совсем то, что вы ищете, но оно может быть преобразовано в ваши цели.Я обнаружил, что использование почтовых индексов намного эффективнее и точнее, чем в городах, потому что города могут охватывать множество почтовых индексов и, следовательно, возвращать данные, которые являются ошибочными для конечного пользователя.

Это все очень ориентировано на США, но может быть легко преобразовано в международныйиспользовать.Я планирую сделать это в какой-то момент в будущем, но еще не нуждался в этом.

1 голос
/ 13 октября 2010

Также рассмотрите, насколько точным должно быть это ... Для малых радиусов (где не требуется большая круговая математика), может быть достаточно просто получить все местоположения в квадрате, который находится на столько миль на стороне.Это можно сделать только с помощью фильтра, без каких-либо вычислений, если у вас есть широта и долгота каждого почтового индекса.И количество возвращаемых строк будет отключено (это будет слишком много) только с коэффициентом 1 - pi / 4, что составляет всего около 21%

для каждой заданной позиции (Tgt lat / long)
- при условии, что радиус указан в морских милях (6080 футов),
, а широта и долгота измеряются в общих минутах
(т. е. 30 градусов, 10 минут = 1810 минут)

, затем:

Select * From theTable
   Where Latitude  Between TgtLat - radius 
                    And TgtLat + radius
     And Longitude Between TgtLong - radius/Cos(TgtLat)
                    And TgtLong + radius/Cos(TgtLat)
0 голосов
/ 13 октября 2010

С каким бы решением вы ни решили, вам понадобится база данных с почтовым индексом. Вот one , загрузите его и импортируйте в таблицу.

0 голосов
/ 13 октября 2010

Я сделал нечто очень похожее с Oracle Spatial, поэтому возьмите мой ответ с небольшим количеством соли, поскольку я не знаком с пространственными особенностями MSSQL:

Я собираюсь исходить из предположения, чтоу вас есть данные, представляющие полигоны, которые охватывают каждый почтовый индекс, все, что вам нужно сделать, это взять свой список почтовых индексов, объединить их полигоны и затем запросить все записи, которые попадают в объединенный многоугольник или в пределах x мильего краев.Некоторые пространственные пакеты имеют тип «multi-gon», который позволит вам комбинировать и работать в несмежных областях (несмежные почтовые индексы в вашем случае).

Если все, что у вас есть, это центральные точки дляПочтовые коды, вы можете сделать то же самое: объединить точки и искать что-нибудь в пределах x миль.Подводный камень в том, что некоторые почтовые индексы могут быть очень большими, и вы потеряете некоторые записи, которые соответствуют вашим критериям, но не находятся в пределах x миль от центральных точек.

Звучитно системы индексации, используемые для пространственных данных, довольно эффективны.

HTH.

0 голосов
/ 13 октября 2010

Проверьте веб-сервис GeoNames. Я использовал это, и это сработало хорошо.

http://www.geonames.org/export/client-libraries.html

0 голосов
/ 13 октября 2010

Ну, у меня есть функция sql, которая выполнит для вас те страшные вычисления, которые на самом деле не такие медленные. Но вот ссылка, а также о том, как выполнить запрос с использованием новых функций в SQL 2008: http://msdn.microsoft.com/en-us/magazine/dd434647.aspx

РЕДАКТИРОВАТЬ: Дополнительные ссылки:

http://blogs.lessthandot.com/index.php/DataMgmt/DataDesign/sql-server-2008-proximity-search-with-th

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...