Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

SELECT com variáveis ​​de consulta que não usam INDEXes


O motivo está no uso de OU condições em ONDE cláusula.

Para ilustrar, tente executar a consulta novamente, desta vez apenas com o id = 5 condição e obter (saída EXPLAIN):
+----+-------------+------------+--------+--------------------+---------+---------+-------+------+----------------+
| id | select_type | table      | type   | possible_keys      | key     | key_len | ref   | rows | Extra          |
+----+-------------+------------+--------+--------------------+---------+---------+-------+------+----------------+
|  1 | PRIMARY     | <derived2> | system | NULL               | NULL    | NULL    | NULL  |    1 |                |
|  1 | PRIMARY     | tree       | const  | PRIMARY,index_both | PRIMARY | 4       | const |    1 |                |
|  2 | DERIVED     | NULL       | NULL   | NULL               | NULL    | NULL    | NULL  | NULL | No tables used |
+----+-------------+------------+--------+--------------------+---------+---------+-------+------+----------------+

E, novamente, desta vez apenas com parent_id = @last_id OR parent_id = 5 condição e obter:
+----+-------------+------------+--------+-----------------+------+---------+------+------+----------------+
| id | select_type | table      | type   | possible_keys   | key  | key_len | ref  | rows | Extra          |
+----+-------------+------------+--------+-----------------+------+---------+------+------+----------------+
|  1 | PRIMARY     | <derived2> | system | NULL            | NULL | NULL    | NULL |    1 |                |
|  1 | PRIMARY     | tree       | ALL    | index_parent_id | NULL | NULL    | NULL |   10 | Using where    |
|  2 | DERIVED     | NULL       | NULL   | NULL            | NULL | NULL    | NULL | NULL | No tables used |
+----+-------------+------------+--------+-----------------+------+---------+------+------+----------------+

O MySQL não é muito bom em lidar com vários índices na mesma consulta. As coisas são um pouco melhores com condições AND; é mais provável ver um index_merge otimização do que uma união de índice otimização.

As coisas estão melhorando conforme as versões avançam, mas testei sua consulta na versão 5.5 , que está na versão de produção mais recente atual, e os resultados são como você descreve.

Para explicar por que isso é difícil, considere:dois índices diferentes responderão por duas condições diferentes da consulta. Um responderá por id = 5 , o outro para parent_id = @last_id OR parent_id = 5 (BTW nenhum problema com o OU dentro do último, uma vez que ambos os termos são tratados a partir do mesmo índice).

Não existe um índice único que possa responder por ambos e, portanto, o FORCE INDEX instrução é ignorada. Veja, FORCE INDEX diz que o MySQL tem que usar um index em uma varredura de tabela. Isso não implica que tenha que usar mais de um índice em uma varredura de tabela.

Então o MySQL segue as regras da documentação aqui. Mas por que isso é tão complicado? Porque para responder usando ambos os índices, o MySQL tem que coletar resultados de ambos, armazenar um de lado em algum buffer temporário enquanto gerencia o segundo. Então é preciso passar por esse buffer para filtrar linhas idênticas (é possível que alguma linha se encaixe em todas as condições). E, em seguida, digitalizar esse buffer para retornar os resultados.

Mas espere, esse buffer em si não está indexado. Filtrar duplicatas não é uma tarefa óbvia. Então o MySQL prefere trabalhar na tabela original e fazer a varredura lá, evitando toda essa bagunça.

Claro que isso é solucionável. Os engenheiros da Oracle ainda podem melhorar isso (recentemente eles têm trabalhado duro para melhorar os planos de execução de consultas), mas não sei se isso está na tarefa TODO ou se tem alta prioridade.