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

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

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

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   

3 ответов


в первом запросе вы соединяете таблицу подписей с предложениями пользователей, получаете много строк, а затем группируете результаты, используя некоторые столбцы из предложений пользователей. Но нет индекса для присоединенной таблицы, чтобы помочь с группировкой, как это должно быть определено в ранее присоединенной таблице. Вместо этого вы должны попытаться создать производную таблицу из пользовательских предложений, которые уже сгруппированы 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 для группировки.


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

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

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


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

если у вас есть в основном только для записи БД (несколько чтений), вы можете воздержаться от использования индексов, поскольку они могут негативно повлиять на производительность записи.