Оптимизация запроса MySQL с внутренним соединением

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

SELECT tblA.id, tblB.id, tblC.id, tblD.id
FROM tblA, tblB, tblC, tblD
INNER JOIN (SELECT max(tblB.id) AS xid
                FROM tblB
                WHERE tblB.rdd = 11305
                GROUP BY tblB.index_id
                ORDER BY NULL) AS rddx
           ON tblB.id = rddx.xid
WHERE
    tblA.id = tblB.index_id
    AND tblC.name = tblD.s_type
    AND tblD.name = tblA.s_name
GROUP BY tblA.s_name
ORDER BY NULL;

Существует связь «один ко многим» между:

  • tblA.id и tblB.index_id
  • tblC.name и tblD.s_type
  • tblD.name и tblA.s_name
+----+-------------+------------+--------+---------------+-----------+---------+------------------------------+-------+------------------------------+
| id | select_type | table      | type   | possible_keys | key       | key_len | ref                          | rows  | Extra                        |
+----+-------------+------------+--------+---------------+-----------+---------+------------------------------+-------+------------------------------+
|  1 | PRIMARY     | derived2   | ALL    | NULL          | NULL      | NULL    | NULL                         | 32568 | Using temporary              |
|  1 | PRIMARY     | tblB       | eq_ref | PRIMARY       | PRIMARY   | 8       | rddx.xid                     |     1 |                              |
|  1 | PRIMARY     | tblA       | eq_ref | PRIMARY       | PRIMARY   | 8       | tblB.index_id                |     1 | Using where                  |
|  1 | PRIMARY     | tblD       | eq_ref | PRIMARY       | PRIMARY   | 22      | tblA.s_name                  |     1 | Using where                  |
|  1 | PRIMARY     | tblC       | eq_ref | PRIMARY       | PRIMARY   | 22      | tblD.s_type                  |     1 |                              |
|  2 | DERIVED     | tblB       | ref    | rdd_idx       | rdd_idx   | 7       |                              | 65722 | Using where; Using temporary |
+----+-------------+------------+--------+---------------+-----------+---------+------------------------------+-------+------------------------------+

person Doug    schedule 02.11.2011    source источник
comment
Какой движок базы данных используют таблицы?   -  person whudson05    schedule 02.11.2011
comment
Пожалуйста, не могли бы вы добавить дополнительную информацию. Например, сколько записей удовлетворяет условию WHERE tblB.rdd = 11305 - действительно ли их 65722? Удаление сгенерированной временной таблицы поможет выполнить запрос, но на самом деле трудно сказать, поскольку мы не знаем, что находится в этих таблицах.   -  person Fenton    schedule 02.11.2011
comment
Спасибо за ответы. Таблицы InnoDB; tblB имеет 41 633 записи с rdd = 11305.   -  person Doug    schedule 03.11.2011
comment
У вас есть индекс (rdd, index_id) в tblB ?   -  person ypercubeᵀᴹ    schedule 03.11.2011


Ответы (2)


Я обновил запрос, используя соединения вместо соединения в предложении WHERE. Также, взглянув на него, как разработчик, вы можете напрямую увидеть взаимосвязь между таблицами. А->В, А->D и D->С. Теперь в таблице B, где вам нужен самый высокий идентификатор на основе общего «ID = Index_ID» И RDD = 11305, не потребуется полный подзапрос. Однако это переместило «MAX ()» в верхнюю часть предложения выбора поля. Я бы удостоверился, что у вас есть индекс в tblB (index_id, rdd). Наконец, выполнение STRAIGHT_JOIN поможет обеспечить порядок выполнения запроса в зависимости от того, как конкретно указано.

-- РЕДАКТИРОВАТЬ ИЗ КОММЕНТАРИЯ --

Похоже, вы получаете нули из tblB. Обычно это указывает на действительную запись tblA, но не на запись tblB с тем же идентификатором, который имеет RDD = 11305. Тем не менее, похоже, что вас интересуют только те записи, которые связаны с 11305, поэтому я соответствующим образом корректирую запрос. Убедитесь, что у вас есть индекс в tblB на основе столбца «RDD» (по крайней мере, в первой позиции в случае индекса с несколькими столбцами)

Как вы можете видеть в этом, я предварительно запрашиваю из таблицы B только 11305 записей и предварительно группирую по index_ID (как связано с tblA). Это дает мне одну запись для каждого индекса, где они будут существовать... Из ЭТОГО результата я присоединяюсь обратно к A, затем снова обратно к B, но на основе этого самого высокого найденного идентификатора совпадения, затем D и C, как было раньше. Итак, СЕЙЧАС вы можете получить любой столбец из любой таблицы и получить соответствующую запись... В этом запросе не должно оставаться значений NULL.

Надеюсь, я объяснил, КАК я собираю кусочки для вас.

SELECT STRAIGHT_JOIN 
      PreQuery.HighestPerIndexID
      tblA.id, 
      tblA.AnotherAField,
      tblA.Etc,
      tblB.SomeOtherField,
      tblB.AnotherField,
      tblC.id, 
      tblD.id
   FROM 
      ( select PQ1.Index_ID,
               max( PQ1.ID ) as HighestPerIndexID
           from tblB PQ1
           where PQ1.RDD = 11305
           group by PQ1.Index_ID ) PreQuery

         JOIN tblA
            on PreQuery.Index_ID = tblA.ID

         join tblB
            on PreQuery.HighestPerIndexID = tblB.ID

         join tblD
            on tblA.s_Name = tblD.name

            join tblC
               on tblD.s_type = tblC.Name
    ORDER BY 
       tblA.s_Name
person DRapp    schedule 02.11.2011
comment
Спасибо вам за помощь. Это предложение не совсем сработало для меня, но заставило меня задуматься в новом направлении. Я думаю, вы уже поняли, но позвольте мне немного пояснить: между таблицами tblA и tblB существует отношение «один ко многим», где для данной записи в tblA будет n связанных записей в tblB, и m из них будут иметь определенный 'rdd'. Целью предложения выбора MAX(tblB.id) является получение самого последнего члена этой связи. Этот запрос, кажется, работает, но я не думаю, что он выбирает запись из tblB с самым высоким «id» для использования в соединении. Надеюсь, это имеет смысл. - person Doug; 03.11.2011
comment
@Doug, это ДОЛЖНО представлять правильно, так как соединение будет извлекать одну запись из каждой таблицы tblA для каждой записи в таблице tblB. Группировка всех неагрегатов должна обеспечивать только самую высокую запись. Теперь проблема. Если вы пытаетесь получить другие столбцы из таблицы B, то MySQL просто захватывает первый экземпляр других столбцов, который может быть тем, с чем вы сталкиваетесь... если это так, я могу настроить запрос, чтобы отразить то, что вы ДЕЙСТВИТЕЛЬНО находясь в поиске. - person DRapp; 03.11.2011
comment
@DRapp- тогда мы будем очень признательны за корректировку;). Я попытался немного сократить запрос, но я действительно получаю другие столбцы из tblB; они отображаются как NULL, когда я использую этот метод. - person Doug; 03.11.2011
comment
@Doug, исправленный ответ и дополнительные разъяснения. - person DRapp; 03.11.2011
comment
@DRapp- Еще раз спасибо. Это больше похоже на то, что я искал; Есть две производные таблицы, в которых указано «использование временного; с помощью файловой сортировки», но запрос кажется быстрее. - person Doug; 09.11.2011

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

EXPLAIN SELECT tblA.id, MAX(tblB.id), tblC.id, tblD.id
FROM tblA
LEFT JOIN tblD ON tblD.name = tblA.s_name
LEFT JOIN tblC ON tblC.name = tblD.s_type
LEFT JOIN tblB ON tblA.id = tblB.index_id
WHERE tblB.rdd = 11305
ORDER BY NULL;

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

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

FLUSH STATUS;

EXPLAIN SELECT tblA.id, MAX(tblB.id), tblC.id, tblD.id
FROM tblA
LEFT JOIN tblD ON tblD.name = tblA.s_name
LEFT JOIN tblC ON tblC.name = tblD.s_type
LEFT JOIN tblB ON tblA.id = tblB.index_id
WHERE tblB.rdd = 11305
ORDER BY NULL;

SHOW SESSION STATUS LIKE 'ha%';

Это дает вам ряд индикаторов, показывающих, что на самом деле произошло при выполнении запроса.

Handler_read_rnd_next - Number of requests to read next row in the data file
Handler_read_key - Number of requests to read a row based on a key
Handler_read_next - Number of requests to read the next row in key order

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

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

person whudson05    schedule 02.11.2011