Slow SQL Query by Limit / Order динамическое поле (координаты от точки X) - PullRequest
1 голос
/ 19 июня 2011

Я пытаюсь сделать SQL-запрос к базе данных из 7 миллионов записей, в базе данных "geonames" индексированы значения "широта" и "долгота" в десятичной системе счисления (10.7), проблема в том, что запрос слишкомslow:

SELECT SQL_NO_CACHE DISTINCT 
       geonameid, 
       name, 
       (6367.41 * SQRT(2 * (1-Cos(RADIANS(latitude)) * Cos(0.704231626533) * (Sin(RADIANS(longitude))*Sin(-0.0669560660943) + Cos(RADIANS(longitude)) * Cos(-0.0669560660943)) - Sin(RADIANS(latitude)) * Sin(0.704231626533)))) AS Distance 
  FROM geoNames 
 WHERE (6367.41 * SQRT(2 * (1 - Cos(RADIANS(latitude)) * Cos(0.704231626533) * (Sin(RADIANS(longitude)) * Sin(-0.0669560660943) + cos(RADIANS(longitude)) * Cos(-0.0669560660943)) - Sin(RADIANS(latitude)) * Sin(0.704231626533))) <= '10') 
ORDER BY Distance

Проблема сортируется по полю «Расстояние», которое при динамическом создании занимает много времени, чтобы просочиться в условие «ГДЕ», если я уберу условие «ГДЕ ... <= 10 "занимает всего 0,34 секунды, но в результате получается 7 миллионов записей, а передача данных из MySQL в PHP занимает почти 120 секунд. </p>

Можете ли вы придумать какой-нибудь способ сделать запрос, чтобы не потерять производительность, ограничиваяПоле расстояния, учитывая, что запрос будет очень часто менять значения?

Ответы [ 3 ]

1 голос
/ 19 июня 2011

Этот тип запроса не может использовать индекс, но должен вычислять, находится ли широта / долгота каждой строки в пределах указанного расстояния. Поэтому типично, что некоторая форма предварительной обработки используется, чтобы ограничить сканирование подмножеством строк. Вы можете создать таблицы, соответствующие «полосам» расстояний (2, 5, 8, 10, 20 миль / км - в зависимости от требований вашего приложения), а затем заполнить эти полосы и поддерживать их в актуальном состоянии. Если вам нужны только те медицинские работники, скажем, или отели, или что-то еще, в пределах 10 миль от заданного местоположения, вам не нужно беспокоиться о тех, кто находится за сотни или тысячи миль. С помощью специальных запросов вы можете, например, выполнить внутреннее объединение в диапазоне «в пределах 10 миль» и тем самым исключить из сканирования сравнения все строки, где вычисленное расстояние> 10. Когда местоположение меняется, «элегантный» способ справиться с этим состоит в следующем: реализовать RTREE, но вы можете определить свой охватывающий регион любым произвольным образом, если хотите, если у вас есть доступ к дополнительным данным - например, с помощью почтовых индексов или округов или штатов.

0 голосов
/ 27 марта 2012

Я придумал:

select * from retailer
where latitude is not null and longitude is not null
and pow(2*(latitude - ?), 2) + pow(longitude - ?, 2) < your_magic_distance_value

Благодаря этому быстрому и простому плоскому коду Лос-Анджелес находится ближе к Гонолулу, чем к Сан-Франциско, но я сомневаюсь, что покупатели учтут это, когда зайдут так далеко за покупками.

0 голосов
/ 19 июня 2011

Есть две вещи, которые вы можете сделать:

  • Убедитесь, что типы данных одинаковы с обеих сторон сравнения: т.е. сравните с 10 (число), а не '10' (тип char) - для БД будет меньше работать
  • . В подобных случаях я создаю представление, что означает, что вычисление выполняется только один раз, даже если вы обращаетесь к нему более одного раза взапрос

Если эти две точки включены в ваш код, вы получите:

CREATE VIEW geoNamesDistance AS
SELECT SQL_NO_CACHE DISTINCT 
       geonameid, 
       name, 
       (6367.41 * SQRT(2 * (1-Cos(RADIANS(latitude)) * Cos(0.704231626533) * (Sin(RADIANS(longitude))*Sin(-0.0669560660943) + Cos(RADIANS(longitude)) * Cos(-0.0669560660943)) - Sin(RADIANS(latitude)) * Sin(0.704231626533)))) AS Distance 
  FROM geoNames;

SELECT * FROM geoNamesDistance
WHERE Distance <= 10
ORDER BY Distance;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...