MySQL Объясните: что вызывает "Использование временного; Использование filesort '

Я планирую создать представление, используя этот SQL SELECT, но объяснение для него показывает его с использованием временного и использования filesort. Я не могу понять, какие индексы мне нужны, чтобы исправить эту проблему. В основном, мне интересно, почему он использует filesort для использования индекса для сортировки.

Вот мои таблицы:

CREATE TABLE `learning_signatures` (
  `signature_id` int(11) NOT NULL AUTO_INCREMENT,
  `signature_file` varchar(100) NOT NULL,
  `signature_md5` varchar(32) NOT NULL,
  `image_file` varchar(100) NOT NULL,
  PRIMARY KEY (`signature_id`),
  UNIQUE KEY `unique_signature_md5` (`signature_md5`)
) ENGINE=InnoDB AUTO_INCREMENT=640 DEFAULT CHARSET=latin1

CREATE TABLE `learning_user_suggestions` (
  `user_suggestion_id` int(11) NOT NULL AUTO_INCREMENT,
  `signature_id` int(11) NOT NULL,
  `ch` char(1) NOT NULL,
  `time_suggested` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `user_id` int(11) NOT NULL,
  PRIMARY KEY (`user_suggestion_id`),
  KEY `char_index` (`ch`),
  KEY `ls_sig_id_indx` (`signature_id`),
  KEY `user_id_indx` (`user_id`),
  KEY `sig_char_indx` (`signature_id`,`ch`)
) ENGINE=InnoDB AUTO_INCREMENT=1173 DEFAULT CHARSET=latin1

И вот проблематичный оператор SQL, который я планирую использовать на мой взгляд:

select ls.signature_id, ls.signature_file, ls.signature_md5, ls.image_file, sug.ch , count(sug.ch) AS suggestion_count
from (`learning_signatures` `ls` left join `learning_user_suggestions` `sug` on(ls.signature_id = sug.signature_id))
group by ls.signature_id, sug.ch;

Результат из объяснения:

id  select_type table   type    possible_keys                   key             key_len ref                 rows    Extra
1   SIMPLE      ls      ALL     NULL                            NULL            NULL    NULL                514     "Using temporary; Using filesort"
1   SIMPLE      sug     ref     ls_sig_id_indx,sig_char_indx    ls_sig_id_indx  4       wwf.ls.signature_id 1

Другой пример, на этот раз с использованием предложения where:

explain select ls.signature_id, ls.signature_file, ls.signature_md5, ls.image_file, sug.ch , count(sug.ch) AS suggestion_count
from (`learning_signatures` `ls` left join `learning_user_suggestions` `sug` on(ls.signature_id = sug.signature_id))
WHERE signature_md5 = '75f8a5b1176ecc2487b90bacad9bc4c'
group by ls.signature_id, sug.ch;

Объяснить вывод:

id  select_type table   type    possible_keys                key                    key_len ref     rows    Extra
1   SIMPLE      ls      const   unique_signature_md5         unique_signature_md5   34      const   1       "Using temporary; Using filesort"
1   SIMPLE      sug     ref     ls_sig_id_indx,sig_char_indx ls_sig_id_indx         4       const   1   

Ответы

Ответ 1

В первом запросе, что вы делаете, присоединяется к вашей таблице подписи с предложениями пользователей, получая множество строк, а затем группируйте результаты, используя некоторые столбцы из предложений пользователей. Но для объединенной таблицы нет индекса, который бы помогал с группировкой, поскольку он должен быть определен в ранее объединенной таблице. Вместо этого вы должны попытаться создать производную таблицу из предложений пользователей, которые уже сгруппированы по ch и signature_id, а затем присоединить к ней:

SELECT ls.signature_id, ls.signature_file, ls.signature_md5, ls.image_file, 
       sug.ch, sug.suggestion_count
FROM learning_signatures ls
LEFT JOIN 
  (SELECT s.signature_id, s.ch, count(s.ch) as suggestion_count
    FROM learning_user_suggestions s 
    GROUP BY s.signature_id, s.ch ) as sug
ON ls.signature_id = sug.signature_id

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

Что касается второго запроса, если вы хотите ограничить подписывание одним, просто добавьте

WHERE ls.signature_md5='75f8a5b1176ecc2487b90bacad9bc4c'

до конца предыдущего запроса и группы только s.ch, потому что только один signature_id будет соответствовать вашему md5 в любом случае. Оптимизатор должен теперь использовать индекс md5 для where и char_index для группировки.

Ответ 2

Возможно, это поможет, если вы создадите индекс для learn_signatures, который содержит как signature_md5, так и signature_id (и в этом порядке)

`KEY `md5_id` (`signature_md5`,`signature_id`)?

Я не эксперт по MySQL, но обнаружил, что созданные ключи, которые инкапсулируют предложение where и предложение join, обычно помогают избавиться от временных и filesort

Ответ 3

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

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