2012-01-31 4 views
1

В моей базе данных есть каталог из примерно 2000 местоположений, разбросанных по всей территории Соединенных Штатов с информацией о zipcode (которую я привязал к координатам lon/lat).SQL Cross Apply Performance Issues

У меня также есть табличную функцию, которая принимает два параметра (ZipCode & Miles) возвращает список соседних почтовых индексов (за исключением такой же почтовый индекс искали)

Для каждого места я пытаюсь получить соседнее расположение идентификаторы. Так что, если место # 4 имеет три места рядом, выход должен выглядеть следующим образом:

То есть, мест 5, 24 и 137 находится в пределах X миль от места 4.

первоначально я пытался использовать перекрестный применять с моей функцией следующим образом:

SELECT A.SL_STORENUM,A.Sl_Zip,Q.SL_STORENUM FROM tbl_store_locations AS A 
CROSS APPLY (SELECT SL_StoreNum FROM tbl_store_locations WHERE SL_Zip in (select zipnum from udf_GetLongLatDist(A.Sl_Zip,7))) AS Q 
WHERE A.SL_StoreNum='04' 

Однако это продолжалось более 20 минут без каких-либо результатов, поэтому я отменил его. Я действительно пытался жестко прописывать в почтовом индексе и он сразу же вернулся список

SELECT A.SL_STORENUM,A.Sl_Zip,Q.SL_STORENUM FROM tbl_store_locations AS A 
CROSS APPLY (SELECT SL_StoreNum FROM tbl_store_locations WHERE SL_Zip in (select zipnum from udf_GetLongLatDist('12345',7))) AS Q 
WHERE A.SL_StoreNum='04' 

Что является наиболее эффективным способом достижения этого списка близлежащих мест? Помня, что в качестве примера здесь я использовал «04», я хочу запустить анализ для 2000 местоположений.

«udf_GetLongLatDist» - это функция, которая использует некоторую математику для вычисления расстояния между двумя географическими координатами и возвращает список zipcodes с расстоянием> 0. Ничто не кажется в нем.

+0

Компонентный комментарий, не связанный напрямую с вашим вопросом, но мы обнаружили, что centroid lat/long для почтовых индексов часто были довольно далеки от места в почтовом индексе, где большинство людей проживает, когда это был сельский почтовый индекс. Таким образом, мы использовали две позиции: zipcode centroid lat/long и lat/long основного города/города, связанные с zipcode. Почтовый индекс считался близким, если любое из этих двух местоположений находилось в пределах x миль от целевого местоположения. – hatchet

ответ

0

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

Рефакторинг запроса, однако, может быть следующим ...

SELECT 
    A.SL_STORENUM, A.Sl_Zip, C.SL_STORENUM 
FROM 
    tbl_store_locations     AS A 
CROSS APPLY 
    dbo.udf_GetLongLatDist(A.Sl_Zip,7) AS B 
INNER JOIN 
    tbl_store_locations     AS C 
    ON C.SL_Zip = B.zipnum 
WHERE 
    A.SL_StoreNum='04' 

Кроме того, производительность CROSS ОТНОСИТЬСЯ принесет большую пользу, если вы можете гарантировать, что ОДС INLINE вместо MULTI-ЗАЯВЛЕНИЕ. Это позволяет расширять udf inline (макрос как) для более чистого плана выполнения.

Это также позволит вам вернуть дополнительные поля из udf. Затем оптимизатор может включать или исключать эти поля из плана в зависимости от того, используете ли вы их на самом деле. В качестве примера можно привести SL_StoreNum, если он легко доступен из запроса в udf, и поэтому удалите необходимость в последнем соединении ...

+0

Это сработало так хорошо! Он пробежал все места примерно за 90 секунд. Можете ли вы объяснить, почему ваши действия были намного лучше моих? Я также посмотрю, как настроен udf. – ElPresidente

+0

@ user1181412 - Основное отличие в том, что ваш использовал 'IN', а мой использовал' JOIN'. И этот мой сохранил вызов функции в том же объеме, что и ваша основная таблица. Это, я подозреваю, означало, что оптимизатор имел более четкое представление об отношениях и построил лучший план. В частности, он, возможно, запускал udf один раз в качестве набора, если он уже является InLine. – MatBailie

1

Когда вы используете эту функцию, вам, вероятно, придется рассчитать каждое возможное расстояние для каждой строки. Вот почему это так долго. Поскольку фактические физические местоположения вообще не двигаются, мы всегда делали предварительное вычисление расстояния от каждого почтового индекса до любого другого почтового индекса (и обновление только раз в месяц или около того, когда мы добавляли новые возможные почтовые индексы). После того, как расстояния предварительно вычислены, все, что вам нужно сделать, это выполнить запрос как

select zip2 from zipprecalc where zip1 = '12345' and distance <=10 
+0

Спасибо. На самом деле это была моя цель создать предварительно скомпилированный список, поэтому мне пришлось бы периодически обновлять его. Кто-то ниже разместил запрос, который работает намного быстрее, чем мой. – ElPresidente

1

У нас есть что-то подобное и оптимизируется его только вычисления расстояния других Почтовые индексы которых широта находится в пределах ограниченного диапазона. Так что если вы хотите другие застежки-молнии внутри @miles, вы используете

where latitude >= @targetLat - (@miles/69.2) and latitude <= @targetLat + (@miles/69.2) 

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

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