Жестоко медленный MySQL Query - PullRequest
       6

Жестоко медленный MySQL Query

2 голосов
/ 16 сентября 2011

Я с радостью приму любой совет по этому поводу - будь то переписывание запроса или настройка таблиц по-другому.

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

Я взял общую идею из этого вопроса: MySQL MIN / MAX возвращает правильное значение, но не информацию о соответствующей записи

Это ответ только для сохранения текущего местоположенияи условие в основной таблице продукта, и сохранить эти таблицы истории, но не использовать их для поиска?Мне нравится идея держать их отдельно, но, конечно, этот запрос выполняется за 50 секунд, что совсем не практично.

SELECT 
'$table' AS tablename, 
$table.id, 
product_name, 
$table.status,
CL.event AS last_event,
CONCAT_WS(' ', CL.location, CL.floor, CL.bin, CL.bay) AS current_loc,
CC.status AS current_cond
FROM $table

LEFT OUTER JOIN
    (SELECT DISTINCT
        C.work_type,
        C.work_id,
        C.status,
        C.inspected_timestamp
        FROM
        (SELECT 
            CONCAT(work_type, work_id) AS condition_id, 
            status,
            MAX(inspected_timestamp) as current
            FROM conditions
            GROUP BY condition_id
        ) XC
    JOIN conditions C
      on CONCAT(C.work_type, C.work_id) = XC.condition_id
      and C.inspected_timestamp = XC.current
    ) CC ON 
    $table.id = CC.work_id AND 
    CC.work_type = '$table'                         

LEFT OUTER JOIN
    (SELECT DISTINCT
        L.work_type,
        L.work_id,
        L.event,
        L.location,
        L.floor,
        L.bin,
        L.bay,
        L.timestamp
        FROM
        (SELECT
            CONCAT(work_type, work_id) AS location_id, 
            location,
            MAX(timestamp) as current
            FROM locations
            GROUP BY location_id
        ) XL
    JOIN locations L
      on CONCAT(L.work_type, L.work_id) = XL.location_id
      and L.timestamp = XL.current
    ) CL ON 
    $table.id = CL.work_id AND 
    CL.work_type = '$table'

HAVING last_event = 'Received'

Я добавляю сюда результаты EXTENDED EXPLAIN.

[0] => Array ( 
    [id] => 1 
    [select_type] => PRIMARY 
    [table] => paintings 
    [type] => ALL 
    [possible_keys] => 
    [key] => 
    [key_len] => 
    [ref] => 
    [rows] => 1159 
    [filtered] => 100.00 
    [Extra] => )

[1] => Array ( 
    [id] => 1 
    [select_type] => PRIMARY 
    [table] => 
    [type] => ALL 
    [possible_keys] => 
    [key] => 
    [key_len] => 
    [ref] => 
    [rows] => 3211 
    [filtered] => 100.00 
    [Extra] => ) 

[2] => Array ( 
    [id] => 1 
    [select_type] => PRIMARY 
    [table] => 
    [type] => ALL 
    [possible_keys] => 
    [key] => 
    [key_len] => 
    [ref] => 
    [rows] => 1870 
    [filtered] => 100.00 
    [Extra] => ) 

[3] => Array ( 
    [id] => 4 
    [select_type] => DERIVED 
    [table] => 
    [type] => ALL 
    [possible_keys] => 
    [key] => 
    [key_len] => 
    [ref] => 
    [rows] => 1868 
    [filtered] => 100.00 
    [Extra] => Using temporary )

[4] => Array ( 
    [id] => 4 
    [select_type] => DERIVED 
    [table] => L 
    [type] => ref 
    [possible_keys] => timestamp 
    [key] => timestamp 
    [key_len] => 8 
    [ref] => XL.current 
    [rows] => 5 
    [filtered] => 100.00 
    [Extra] => Using where ) 

[5] => Array ( 
    [id] => 5 
    [select_type] => DERIVED 
    [table] => locations 
    [type] => ALL 
    [possible_keys] => 
    [key] => 
    [key_len] => 
    [ref] => 
    [rows] => 3913 
    [filtered] => 100.00 
    [Extra] => Using temporary; Using filesort ) 

[6] => Array ( 
    [id] => 2 
    [select_type] => DERIVED 
    [table] => 
    [type] => ALL 
    [possible_keys] => 
    [key] => 
    [key_len] => 
    [ref] => 
    [rows] => 3191 
    [filtered] => 100.00 
    [Extra] => Using temporary ) 

[7] => Array ( 
    [id] => 2 
    [select_type] => DERIVED 
    [table] => C 
    [type] => ref 
    [possible_keys] => inspected_timestamp 
    [key] => inspected_timestamp 
    [key_len] => 8 
    [ref] => XC.current 
    [rows] => 45 
    [filtered] => 100.00 
    [Extra] => Using where ) 

[8] => Array (
     [id] => 3 
    [select_type] => DERIVED 
    [table] => conditions 
    [type] => index 
    [possible_keys] => 
    [key] => work_type_2 
    [key_len] => 316 
    [ref] => 
    [rows] => 3986 
    [filtered] => 100.00 
    [Extra] => Using index; Using temporary; Using filesort )

Ответы [ 3 ]

1 голос
/ 17 сентября 2011

Я помещаю это в ответ исключительно из-за ограничения длины комментария.

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

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

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

Кроме того, пункт имения в конце по определению не будет оптимизирован. Если есть способ перенести его в условие where или в качестве критерия в одном из объединений, у вас есть шанс значительно улучшить план запроса, но, учитывая стоимость сводок, это все равно займет некоторое время.

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

1 голос
/ 16 сентября 2011

Вы можете сделать несколько вещей:

  1. ОБЪЯСНИТЕ ПЛАН по запросу.Посмотрите, есть ли там где-нибудь СТОЛ.Это убийца.
  2. Посмотрите, если перестановка запроса имеет значение в результатах EXPLAIN PLAN.Ранняя фильтрация большего количества записей уменьшит необходимое время.
  3. Убедитесь, что столбцы в каждом предложении WHERE имеют индекс.
  4. Чем больше записей, тем длиннее запрос.Сколько истории вы сохраняете?Сколько строк вы говорите?У вас должна быть политика, которая удаляет записи старше вашей пороговой величины хранения и помещает их в историю или схему отчетов.
  5. Можете ли вы воспользоваться триггерами или представлениями для предварительного вычисления любого из этих значений?
0 голосов
/ 18 сентября 2011

Как объяснил @gview, есть множество вещей, которые помогают этому запросу быть жестоко медленным. Помимо всех упомянутых в его ответе, есть также функция CONCAT() в двух таблицах, где результаты позже используются для соединения этих двух производных таблиц.

Если вы просто хотите показать строки таблицы product только с самой последней связанной строкой в ​​location и последней связанной строкой в ​​condition, вы можете использовать что-то вроде следующего (это имеет только логику для последний condition, вам понадобится еще один аналог LEFT JOIN для самого последнего location):

SELECT 
  t.id, 
  t.product_name, 
  t.status,
  cc.status AS current_cond
FROM 
      $table AS t
  LEFT OUTER JOIN
      ( SELECT c.*
        FROM 
              conditions AS c
          JOIN
              ( SELECT 
                  work_id, 
                  MAX(inspected_timestamp) as current_ts
                FROM conditions mc
                WHERE work_type = '$table'
                GROUP BY condition_id
              ) AS mc
            ON  mc.work_id = c.work_id
            AND mc.current_ts = c.inspected_timestamp 
        WHERE c.work_type = '$table'
      ) AS cc  
    ON cc.work_id = t.id                     
...