Nano Hash - криптовалюты, майнинг, программирование

Масштабируемость Местоположение Расстояние Поиск более 100 000 позиций по широте и долготе в США

Сценарий =

1) Офисы доставки, расположенные по всей территории США, каждый указывает свой собственный максимальный радиус доставки в милях.

2) Целевой адрес, преобразованный в LatLng, является местом доставки.

Цель = вернуть набор данных отделений доставки (1), предел радиуса доставки которых находится в пределах этого расстояния до целевого адреса (2).

Попытка =

В качестве отправной точки для решения своей проблемы я использую отличный рабочий пример консалтинговой компании Storm по определению ближайшего к клиенту офиса: Хаверсинус расстояние между двумя точками

Моя таблица «Офисы» хранит адрес офиса вместе с их значениями широты и долготы, а также их максимальное расстояние «deliveryLimit».

SQL для вычисления хаверсинуса поражает меня и в настоящее время находится за пределами моего понимания!
Storm SQL приведен ниже, но вместо того, чтобы выбирать только одну строку из прямых вычислений расстояния, мне нужно выбрать все строки офисов, для которых максимальное расстояние доставки составляет меньше, чем расстояние между офисом и заказчиком.

Вопрос1 = Как мне добавить фильтр ограничения максимального расстояния в запрос SQL, чтобы он возвращал офисы с зоной доставки, включающей целевое местоположение?

Вопрос2 = Как я могу ограничить количество запрашиваемых офисов теми, которые реально могут находиться в целевой области США? Например, если целевым местоположением является Бойсе, штат Айдахо, а офисы в Лос-Анджелесе, штат Калифорния, имеют ограничение на дальность доставки в 300 миль. Нет смысла даже опрашивать такие конторы. Однако офисы в Вашингтоне; Штаты Орегон и Северная Невада, граничащие с Айдахо, должны быть включены в поисковый запрос, поскольку некоторые из них могут иметь максимальные значения расстояния, которые достигаются в этом примере Бойсе, ID.

SQL для Haversine, используемый Storm:

SELECT TOP 1 *, ( 3960 * acos( cos( radians( @custLat ) ) *
cos( radians( Lat ) ) * cos( radians(  Lng  ) - radians( @custLng ) ) +
sin( radians( @custLat ) ) * sin( radians(  Lat  ) ) ) ) AS Distance
FROM Offices
ORDER BY Distance ASC

В приведенном выше примере SQL выбирается только ближайший к целевой широте / долготе офис (@custLng)

Шторм подошел к расчету дистанции с двух разных сторон. Приведенный выше SQL был первым. Во-вторых, координаты офиса сохраняются в списке в памяти и создается метод с функцией для обхода списка, вычисляя расстояния и, наконец, выбирая ближайший, например:

/// <summary>
/// Returns the distance in miles or kilometers of any two
/// latitude / longitude points.
/// </summary>
/// <param name="pos1">Location 1</param>
/// <param name="pos2">Location 2</param>
/// <param name="unit">Miles or Kilometers</param>
/// <returns>Distance in the requested unit</returns>
public double HaversineDistance(LatLng pos1, LatLng pos2, DistanceUnit unit)
{
    double R = (unit == DistanceUnit.Miles) ? 3960 : 6371;
    var lat = (pos2.Latitude - pos1.Latitude).ToRadians();
    var lng = (pos2.Longitude - pos1.Longitude).ToRadians();
    var h1 = Math.Sin(lat / 2) * Math.Sin(lat / 2) +
              Math.Cos(pos1.Latitude.ToRadians()) * 
              Math.Cos(pos2.Latitude.ToRadians()) *
              Math.Sin(lng / 2) * Math.Sin(lng / 2);
    var h2 = 2 * Math.Asin(Math.Min(1, Math.Sqrt(h1)));
    return R * h2;
}

public enum DistanceUnit { Miles, Kilometers };

и

var Offices = GetMyOfficeList();
for(int i = 0; i< Offices.Count; i++)
{
    Offices[i].Distance = HaversineDistance(
                        coord,
                        new LatLng(Offices[i].Lat, Offices[i].Lng),
                        DistanceUnit.Miles);
}

var closestOffice = Offices.OrderBy(x => x.Distance).Take(1).Single();

Масштабируемость важна, поскольку в моем сценарии может легко оказаться намного больше, чем 100 000 офисов, поэтому вариант со списком офисов в памяти маловероятен!

05.10.2014

  • Используете ли вы Sql2008 или новее, и если да, можно ли сохранить местоположение вашего офиса в виде геометрических данных? Если у вас есть много вариантов, перед вами откроется множество вариантов, которые значительно упростят эту задачу. 06.10.2014
  • Вы также можете довольно просто уменьшить количество офисов, в которых вам нужно выполнять дорогостоящий запрос, например выбор квадрата размером 600 миль или чуть больше с использованием границ широты и длинных чисел офиса в качестве подзапроса, а затем ввод результатов этого в более сложный расчет. Если вы хотите сделать это по разделам, то, например, разделите страну на квадраты по 600 миль: назначьте каждый офис квадрату, создайте таблицу с указанием соседних квадратов, найдите квадрат вашего места доставки, затем вычислите набор офисов в этом квадрате и всех смежных квадратах для запроса. 06.10.2014
  • Скотт - да, с использованием Sql2008 или новее. Уверены, что можно сохранить каждое местоположение офиса в виде геометрических данных - конечно, интересно узнать больше о том, какие варианты доступны, чтобы упростить задачу? 06.10.2014
  • Rup - это верный способ сократить количество запросов - спасибо за то, что поделились информацией. 06.10.2014
  • Какая у вас версия sql? В 2012 году они добавили кое-что нового, что поможет мне написать ответ. 06.10.2014
  • Скотт - это VPS через хостинг Arvixe - в настоящее время SQL Server 2008R2 и IIS7, и в ближайшее время не предвидится никакой миграции на 2012 год. 06.10.2014

Ответы:


1

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

Я собираюсь сделать некоторые предположения о структуре вашей таблицы, но главное, у вас есть Location и DeleveryArea

create table Offices
(
  OfficeName varchar(40),
  Location geography,
  DeliveryDistance float, --stored in miles
  --If you are on SQL2008 or 2008R2 replace BufferWithCurves with one of the older Buffer functions
  DeliveryArea as Location.BufferWithCurves(DeliveryDistance * 1609.34) PERSISTED, --1609.34 converts miles to meters 
)

В моем примере выше я использовал BufferWithCurves, но это доступно только на Sql2012 и новее, если вы используете 2008 или 2008R2, вам нужно будет использовать либо BufferWithTolerance или STBuffer или просто вручную укажите свою область с помощью передать статус вставки.

Теперь заполним данные, поскольку мы сделали DeliveryArea вычисляемый постоянный столбец, это на самом деле довольно легко сделать. Все, что вам нужно сделать, это указать местоположение офиса и радиус его зоны доставки, и он рассчитает круг для этой площади для вас. Я буду использовать примеры, которые вы указали в своем вопросе:

insert into Offices (OfficeName, Location, DeliveryDistance) 
values ('Boise, ID', 
        geography::Point(43.6187102,-116.2146068, 4326), --4326 represents a "lat and long" coordinate system
        300
       )

insert into Offices (OfficeName, Location,DeliveryDistance) 
values ('LA, CA', 
        geography::Point(34.0204989,-118.4117325, 4326),
        300
       )

insert into Offices (OfficeName, Location,DeliveryDistance) 
values ('Walla Walla, WI', 
        geography::Point(46.0627549,-118.3337259, 4326),
        300
       )

insert into Offices (OfficeName, Location,DeliveryDistance) 
values ('Baker City, OR', 
        geography::Point(44.7746169,-117.8317284, 4326),
        300
       )

insert into Offices (OfficeName, Location,DeliveryDistance) 
values ('Elko, NV', 
        geography::Point(40.846931,-115.7669825, 4326),
        300
       )

Теперь к вашему запросу, если вы хотите найти, какие области доставки обслуживаются Джордан Валли, Орегон (42.9740245, -117.0533247), ваш запрос будет просто

declare @cust geography = geography::Point(42.9740245,-117.0533247, 4326)

SELECT OfficeName, 
       Location.STDistance(@cust) / 1609.34 AS Distance, --convert back to miles
       Location.Lat as Lat,
       Location.Long as Lng
FROM Offices
where  DeliveryArea.STContains(@cust) = 1
ORDER BY Distance asc

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

С хорошо спланированным пространственным индексом это решение должно работать даже для вашего набора записей 100 000 местоположений. Если вы хотите узнать больше о некоторых преимуществах использования geography, см. этот ТАК вопрос и ответ.

Вот скрипт SQL со всеми запросами, которые я поставил выше, показанными в рабочем примере .

06.10.2014
  • Скотт - ВАУ! Это тот ответ и детали, которые не только помогают мне визуализировать то, о чем вы говорите, но и обязательно помогут многим другим, кто наткнулся на этот пост. Спасибо, Скотт, за время и детали, которые вы вложили в это, я очень ценю это. Я собираюсь запустить тесты и опробовать каждый путь решения ценных ответов на этой странице, прежде чем проверять свой предпочтительный ответ - так что пройдет некоторое время, прежде чем я закрою этот. Еще раз спасибо всем, кто прислал помощь :) 06.10.2014
  • @ MartinSansone-MiOEE просто обязательно прочитайте, как делать пространственные индексы, прежде чем запускать тесты, и обязательно добавьте их в свои тестовые таблицы. Как и любые другие нормальные индексы, если вы настроите их неправильно, они не будут использоваться в ваших запросах, не оказывая никакой помощи, а просто увеличивают нагрузку на базу данных. 07.10.2014

  • 2

    У вас уже есть расстояние между офисом и целевой локацией. Возвращать только те офисы, расстояние до которых меньше или равно их радиусу доставки:

    SELECT * FROM (SELECT Id, DeliveryRadius, ( 3960 * acos( cos( radians( @custLat ) ) *
        cos( radians( Lat ) ) * cos( radians(  Lng  ) - radians( @custLng ) ) +
        sin( radians( @custLat ) ) * sin( radians(  Lat  ) ) ) ) AS Distance
        FROM Offices)
    WHERE Distance <= DeliveryRadius
    ORDER BY Distance ASC
    

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

    05.10.2014
  • Джейсон, спасибо за улучшение и за то, что вы показали мне, куда нужно добавить дополнительный Select. Я предвижу, что запрос запроса будет вызываться много раз в течение короткого промежутка времени, поэтому я беспокоюсь о производительности по мере роста набора данных, поэтому я заинтересован в настройке архитектуры с самого начала, чтобы избежать вероятного узкого места. Я создам набор образцов данных и проведу тесты, как вы предлагаете :) 06.10.2014

  • 3

    Для @Raduis в милях:

    DECLARE @RadiusInDegrees decimal(18,15)
    SET @RadiusInDegrees = @Radius / 69.11 * 1.4142
    

    @Radius / 69.11 дает радиус в градусах, но его нужно умножить на корень 2, чтобы покрыть всю ограничивающую площадь квадрата.

    SELECT
        @LatitudeLow = @Latitude - @RadiusInDegrees,
        @LatitudeHigh = @Latitude + @RadiusInDegrees,       
        @LongitudeLow = @Longitude - @RadiusInDegrees,
        @LongitudeHigh = @Longitude + @RadiusInDegrees
    

    Использование ограничивающей области ограничивает количество дорогостоящих вычислений точного расстояния.

    [...]
    WHERE
        [Latitude] > @LatitudeLow  
        AND [Latitude] < @LatitudeHigh 
        AND [Longitude] > @LongitudeLow
        AND [Longitude] < @LongitudeHigh                
        AND [your exact radius calculation] <= 
          (CASE WHEN [DeliverlyLimit] < @Radius THEN 
              [DeliveryLimit] ELSE @Radius END)
    

    Также для повышения производительности не используйте SELECT * или подзапросы, если можно этого избежать. Вернуть только идентификаторы и создать индекс покрытия для идентификатора, широты, долготы и лимита доставки. Кэшируйте все данные из вашей таблицы в массив объектов, где индекс является идентификатором, поэтому сопоставление из набора результатов выполняется быстро.

    06.10.2014
  • PJ7 Спасибо за ваше решение, мне особенно нравятся ваши советы по сохранению строгости полей выбора и советы по производительности :) 06.10.2014
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

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

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

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

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..