Индексирование таблицы mysql для поиска гео с использованием широты / долготы - PullRequest
5 голосов
/ 09 мая 2011

У меня есть устаревший список таблиц innodb, содержащий бизнес с широтой / долготой. При заданной широте / долготе (ниже 51.2167 / 4.41667) запрос должен вернуть первые 30 активных, включенных, не удаленных 30 предприятий в порядке близости (километров). Соединение с таблицей счетов проводится для проверки действительности листинга.

select 
    listing.*
from
    listing listing ,
    account account 
where
    listing.account_id = account.id 
    and listing.active = 1 
    and listing.approved = 1 
    and listing.deleted = 0 
    and listing.enabled = 1 
    and account.enabled = 1 
    and account.activated_by_user = 1 
group by
    listing.id
having
     111.222569*degrees(acos(sin(radians(listing.latitude))*sin(radians( 51.2167)) +cos(radians(listing.latitude))*cos(radians( 51.2167))*cos(radians(listing.longitude - 4.41667)))) < 250
order by
     111.222569*degrees(acos(sin(radians(listing.latitude))*sin(radians( 51.2167)) +cos(radians(listing.latitude))*cos(radians( 51.2167))*cos(radians(listing.longitude - 4.41667))))
limit 30;

Каждая таблица Listing и Account содержит более 50000 строк, но выполнение запроса по-прежнему занимает в среднем 24 секунды. Без заказа, это занимает 17 секунд.

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

+----+-------------+---------+-------------+-------------------------------------------------------------------------------------------------+-----------------------------------------------------------------+---------+------------------------+------+--------------------------------------------------------------------------------------------------------------------------------+
| id | select_type | table   | type        | possible_keys                                                                                   | key                                                             | key_len | ref                    | rows | Extra                                                                                                                          |
+----+-------------+---------+-------------+-------------------------------------------------------------------------------------------------+-----------------------------------------------------------------+---------+------------------------+------+--------------------------------------------------------------------------------------------------------------------------------+
|  1 | SIMPLE      | listing | index_merge | FKB4DC521D9306A80C,listing_active,listing_approved,listing_enabled,listing_deleted,index_test_1 | listing_active,listing_approved,listing_enabled,listing_deleted | 1,1,1,1 | NULL                   | 3392 | Using intersect(listing_active,listing_approved,listing_enabled,listing_deleted); Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | account | eq_ref      | PRIMARY,account_enabled,account_activated_by_user,index_test_2                                  | PRIMARY                                                         | 8       | ctm.listing.account_id |    1 | Using where                                                                                                                    |
+----+-------------+---------+-------------+-------------------------------------------------------------------------------------------------+-----------------------------------------------------------------+---------+------------------------+------+--------------------------------------------------------------------------------------------------------------------------------+

Любая помощь очень ценится.

Ответы [ 2 ]

8 голосов
/ 09 мая 2011

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

Можете ли вы ограничить диапазон поиска? Вы, наверное, заметили, что в большинстве веб-приложений для поиска магазинов есть пункт выпадающего меню, в котором можно выбрать «в пределах 5 миль», «в пределах 10 миль» и т. Д.

Если вы МОЖЕТЕ сделать это, вы должны добавить в свой запрос предложение WHERE и оптимизировать его, поместив индекс в столбец LATITUDE. Допустим, вы используете значение RANGELIMIT для ограничения диапазона поиска в милях.

Попробуйте это предложение

WHERE LISTING.LATITUDE BETWEEN LOCATION.LATITUDE - (RANGELIMIT * 1.1508/60) 
                           AND LOCATION.LATITUDE + (RANGELIMIT * 1.1508/60)

Это работает, потому что морская миля почти точно равна одной минуте (1/60) градуса широты. Коэффициент 1.1508 переводит морские мили в уставные мили.

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

Вы также можете включить предложение BETWEEN по долготе. Но по моему опыту простое выполнение широты МЕЖДУ поиском дает вам отличные результаты.

1 голос
/ 09 мая 2011

Возможно, вы захотите взглянуть на этот вопрос .К сожалению, вы не можете создать индекс SPATIAL для столбцов, которые не относятся к типу GEOMETRY, поэтому по крайней мере вам потребуется добавить один столбец в таблицу.

...