2016-10-04 3 views
2

У меня есть несколько таблиц с ~ 10 миллионами строк. Время выполнения этого запроса составляет около ~ 10 минут:Оптимизация Mysql в одном запросе

SELECT 
    c.id, 
    CONCAT_WS(', ', country.name, region.name, c.name) AS [text] 
FROM city_name AS cn 
    INNER JOIN city AS c 
     ON c.id = cn.city_id 
    INNER JOIN country 
     ON country.id = cn.country_id 
    INNER JOIN region 
     ON region.id = cn.region_id 
WHERE cn.name LIKE '%:q%' 
GROUP BY cn.city_id 
LIMIT 50 

Но когда я делаю два запросов его казни около 5 секунд:

Первый:

SELECT 
    city_id 
FROM city_name 
WHERE name LIKE '%:q%' 
GROUP BY city_id 
LIMIT 50 

Вторая:

SELECT 
    c.id, 
    CONCAT_WS(', ',country.name,region.name,c.name) AS text 
FROM city AS c 
    INNER JOIN country 
     ON country.id = c.country_id 
    INNER JOIN region 
     ON region.id = region_id 
WHERE c.id IN (:ids) 

Как я могу оптимизировать его в одном запросе?

Спасибо.

+0

Вы проверили план объяснения – khalid

+0

Вы используете 'LIKE = '% .....%'', который не может использовать какой-либо индекс (поскольку индексы строк не индексируют всю строку, а скорее подмножество). Не зная цели этого столбца, я не могу дать никакой точной рекомендации, но в целом попытаюсь переписать его как 'LIKE = '...%'' (префикс). – apokryfos

+0

В отсутствие каких-либо агрегационных функций предложение GROUP BY довольно бессмысленно. Аналогично, предложение LIMIT без предложения ORDER BY. – Strawberry

ответ

3

Вы пробовали внутренний запрос вроде этого.

SELECT 
    c.id, CONCAT_WS(', ',country.name,region.name,c.name) AS text 
FROM 
    city AS c 
INNER JOIN 
    country ON country.id = c.country_id 
INNER JOIN 
    region ON region.id = region_id 
WHERE 
    c.id IN (
     SELECT 
      city_id 
     FROM 
      city_name 
     WHERE 
      name LIKE '%:q%' 
     GROUP BY 
      city_id 
     LIMIT 50 
    ) 

Или попробуйте переместить фильтр cn.name LIKE '%:q%' откуда клауса в ON-Клауса из первых РЕГИСТРИРУЙТЕСЬ

1

Я более хорошо разбираюсь в T-SQL, так что простите меня, если я немного приму немного синтаксиса. Надеюсь, концепция понятна.

SELECT 
    c.id, CONCAT_WS(', ',country.name,region.name,c.name) AS text 
FROM 
    (SELECT city_id 
    FROM city_name 
    WHERE name LIKE '%:q%' 
    GROUP BY city_id) AS c 
INNER JOIN 
    country ON country.id = c.country_id 
INNER JOIN 
    region ON region.id = region_id 
WHERE 
    c.id IN (:ids) 

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

YMMV, но эта стратегия, хотя и не очень красивая, хорошо работала для меня в прошлом. В T-SQL я также обычно делаю первый результат в таблице temp или CTE, но я не уверен, как это работает в MySql, поэтому рассмотрим некоторые дополнительные исследования в этих областях для повышения производительности.

-1

"Normalize, но не overnormalize".

Нормативный город + страна + район обычно пример переназначения. Рекомендовать иметь одну таблицу с тремя столбцами, а не 3-таблицу JOIN. Также рекомендуем использовать стандартный 2-буквенный код country_code CHAR(2) CHARACTER SET ascii.

Смежные вопросы