2014-02-25 4 views
0

Ниже запрос занимает много времени, часто регистрируя медленный журнал.Любой возможный способ переписать этот запрос для лучшей производительности?

Есть ли какой-либо возможный способ переписать ниже запрос, я имею в виду лучше, чем текущий запрос.

select 
    p . *, 
    pt.pretty_name, 
    pt.seo_name, 
    pt.description, 
    pt.short_description, 
    pt.short_description_2 
from 
    cat_products p, 
    cat_product_catalog_map pcm, 
    cat_current_product_prices cpp, 
    cat_product_text pt 
where 
    pcm.catalog_id = 2 
     and pcm.product_id = p.product_id 
     and p.owner_catalog_id = 2 
     and cpp.product_id = p.product_id 
     and cpp.currency = 'GBP' 
     and cpp.catalog_id = 2 
     and cpp.state <> 'unavail' 
     and pt.product_id = p.product_id 
     and pt.language_id = 'EN' 
     and p.product_id not in (select distinct 
      product_id 
     from 
      cat_product_detail_map 
     where 
      short_value in ('ft_section' , 'ft_product')) 
order by pt.pretty_name 
limit 200 , 200; 
+5

вы должны пойти на «присоединиться», проверить это http://stackoverflow.com/questions/17946221/sql-join-and-different-types-of-joins – Lab

+0

Зависит от многих. Что вы индексируете определение? Если вы могли бы опубликовать DDL для используемых таблиц и объяснения, это было бы намного легче помочь. –

+0

У меня есть правильные индексы в столбцах – user3048109

ответ

2

Во-первых, я хотел бы перейти к ANSI 92 Явные синтаксис, а не ANSI 89 неявный синтаксис объединения, который вы используете, как следует из названия это более 20 лет неактуальна:

select ... 
from cat_products p 
     INNER JOIN cat_product_catalog_map pcm 
      ON pcm.product_id=p.product_id 
     INNER JOIN cat_current_product_prices cpp 
      ON cpp.product_id = p.product_id 
     INNER JOIN cat_product_text pt 
      ON pt.product_id=p.product_id 
WHERE .... 

Это не повлияет на производительность, но сделает ваш запрос более четким и менее подвержен случайным кросс-соединениям. Аарон Бертран написал good article on the reasons to switch, который стоит прочитать (он нацелен на SQL Server, но многие принципы универсальны). Тогда я бы удалил NOT IN (Subquery) MySQL не оптимизирует подзапросы, подобные этой скважине. Он перепишет его в:

AND NOT EXISTS (SELECT 1 
       FROM cat_product_detail_map 
       WHERE short_value in ('ft_section','ft_product') 
       AND  cat_product_detail_map.product_id = p.product_id 
       ) 

Затем он выполнит этот подзапрос один раз для каждой строки. Инверсия этого сценария (WHERE <expression> IN (Subquery) описан в статье Optimizing Subqueries with EXISTS Strategy)

Вы можете исключить эти product_ids используя LEFT JOIN/IS NULL метод, который работает лучше в MySQL, поскольку это позволяет избежать подзапрос полностью:

SELECT ... 
FROM cat_products p 
     LEFT JOIN cat_product_detail_map exc 
      ON exc.product_id = p.product_id 
      AND exc.short_value in ('ft_section','ft_product') 
WHERE exc.product_id IS NULL 

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

Таким образом, ваш полный запрос будет таким:

SELECT p.*, 
     pt.pretty_name, 
     pt.seo_name, 
     pt.description, 
     pt.short_description, 
     pt.short_description_2 
FROM cat_products p 
     INNER JOIN cat_product_catalog_map pcm 
      ON pcm.product_id = p.product_id 
     INNER JOIN cat_current_product_prices cpp 
      ON cpp.product_id = p.product_id 
     INNER JOIN cat_product_text pt 
      ON pt.product_id = p.product_id 
     LEFT JOIN cat_product_detail_map exc 
      ON exc.product_id = p.product_id 
      AND exc.short_value in ('ft_section','ft_product') 
WHERE exc.product_id IS NULL 
AND  pcm.catalog_id = 2 
AND  p.owner_catalog_id = 2 
AND  cpp.currency = 'GBP' 
AND  cpp.catalog_id = 2 
AND  cpp.state <> 'unavail' 
AND  pt.language_id = 'EN' 
ORDER BY pt.pretty_name limit 200,200; 

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

0

Выполнение запроса зависит от следующих факторов:

  • индексов.
  • Структура самого запроса.

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

О запросе, вы можете использовать оператор JOIN, чтобы упростить:

select 
    p.*, 
    pt.pretty_name, 
    pt.seo_name, 
    pt.description, 
    pt.short_description, 
    pt.short_description_2 
from 
    cat_products p 
    join cat_product_catalog_map pcm on p.product_id = pcm.product_id 
    join cat_current_product_prices cpp on p.product_id = cpp.product_id 
    join cat_product_text pt on p.product_id = pt.product_id 
    join (select distinct product_id from cat_product_detail_map 
    where short_value NOT in ('ft_section' , 'ft_product') otherProdId on otherProdId.product_id = p.product_id 
where 
    pcm.catalog_id = 2 
     and p.owner_catalog_id = 2 
     and cpp.currency = 'GBP' 
     and cpp.catalog_id = 2 
     and cpp.state <> 'unavail' 
     and pt.language_id = 'EN' 
order by pt.pretty_name 
limit 200,200; 
0

Для начала: Ваше заявление не читаемое, как это должно быть. Поскольку вы не используете синтаксис ANSI join, вам нужно посмотреть, как связаны таблицы. Мое первое предположение заключалось в том, что вы связываете таблицу с таблицей, чтобы получить от cat_products до cat_product_text. Это оказалось не так.

Что вы делаете, это выбрать поля только из cat_products и cat_product_text. Так зачем вообще соединяться с двумя другими таблицами? Вы пытаетесь увидеть, существуют ли записи в этих таблицах? Затем используйте для этого предложение EXISTS.

При объединении таблиц в каталоге id, затем показывайте dbms, что это связывает ваши таблицы (pcm.catalog_id = p.owner_catalog_id), чем кажется, что они не связаны (pcm.catalog_id = 2, p.owner_catalog_id = 2).

Вам не нужно использовать DISTINCT для запроса IN. Dbms должен решить сам по себе, если имеет смысл удалить дубликаты или нет в этом наборе.

Следующий запрос не обязательно дает то же самое, что и исходный запрос. Это зависит от цели соединения таблиц cat_product_catalog_map и cat_current_product_prices. Я предполагаю, что это просто проверка на существование. Таким образом, запрос может быть переписан следующим образом. Это должно быть быстрее, потому что вам не нужно присоединяться к таблицам, которые не добавляют поля к результату. Но даже это зависит от размеров стола и т.д.

SELECT 
    p.*, 
    pt.pretty_name, 
    pt.seo_name, 
    pt.description, 
    pt.short_description, 
    pt.short_description_2 
FROM cat_products p 
JOIN cat_product_text pt ON pt.product_id = p.product_id and pt.language_id = 'EN' 
WHERE p.owner_catalog_id = 2 
AND p.product_id NOT IN 
(
    SELECT product_id 
    FROM cat_product_detail_map 
    WHERE short_value in ('ft_section','ft_product') 
) 
AND EXISTS 
(
    SELECT * 
    FROM cat_product_catalog_map pcm 
    WHERE pcm.product_id = p.product_id 
    AND pcm.catalog_id = p.owner_catalog_id 
) 
AND EXISTS 
(
    SELECT * 
    FROM cat_current_product_prices cpp 
    WHERE cpp.product_id = p.product_id 
    AND cpp.catalog_id = p.owner_catalog_id 
    AND cpp.currency = 'GBP' 
    AND cpp.state <> 'unavail' 
) 
ORDER BY pt.pretty_name LIMIT 200,200; 
Смежные вопросы