Tenho uma estrutura de tabela da seguinte forma:
CREATE TABLE `sale_product_inventories` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`sale_id` int(11) NOT NULL,
`product_id` int(11) NOT NULL,
`size` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,
`tier_number` int(11) NOT NULL DEFAULT '1',
`sale_product_pool_id` int(11) DEFAULT NULL,
`inventory` int(11) NOT NULL,
`in_cart_units` int(11) DEFAULT '0',
`size_display_order` tinyint(4) NOT NULL DEFAULT '0',
`last_updated_by` int(11) DEFAULT '0',
`created_by` int(11) DEFAULT '0',
`status` enum('active','inactive') COLLATE utf8_unicode_ci NOT NULL DEFAULT 'active',
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `UNIQUE` (`sale_id`,`product_id`,`tier_number`,`size`,`sale_product_pool_id`)
) ENGINE=InnoDB AUTO_INCREMENT=92872 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
OBS: Tenho um Index UNIQUE = sale_id
, product_id
, tier_number
, size
,sale_product_pool_id
Quando eu executo esta consulta:
select * from sale_product_inventories
where
sale_id in (502,504) and
(sale_id, product_id) in ((502,2),(502,1), (502,3),(502,4) ,(504,2) ,(504,3) )
MySql usa o índice Unique e o tempo de execução é de 0,7 milissegundos
MAS
quando executo esta consulta
select * from sale_product_inventories
where
(sale_id, product_id) in ((502,2),(502,1), (502,3),(502,4) ,(504,2) ,(504,3) )
O MySql não usa o índice UNIQUE e o tempo de execução é de 76 milissegundos.
Mysql: 5.5.27 Versão InnoDB: 1.1.8
Minha pergunta é por que o mysql está se comportando dessa maneira. Alguém por favor pode me ajudar com isso.
EDIT:
me deparei com isso, então pensei que poderia ser útil adicionar MySQL geralmente não pode usar índices em colunas, a menos que as colunas sejam isoladas na consulta. “Isolar” a coluna significa que ela não deve fazer parte de uma expressão ou estar dentro de uma função na consulta.
O otimizador MySQL não pode otimizar expressões neste formato:
Não é uma questão de acertar os índices -- parece que não foi implementado.
O otimizador não entende que isso é equivalente a...
... ou ...
Existe o Bug #35819 , que encontrei originalmente neste artigo , que por sua vez foi mencionado nos comentários deste post .
Infelizmente, não os encontrei até que já tivesse quebrado o novo Optimizer Trace no MySQL 5.6 e executado alguns casos de teste por meio dele. Parecia uma aposta segura que, se o 5.6 não pudesse lidar com isso, as versões anteriores também não poderiam.
Acontece que o MySQL 5.6 realmente não pode lidar com isso. A construção "conjunto em conjunto de conjuntos" parece simplesmente não ser algo que o otimizador capta. Portanto, neste caso, não é uma questão de o otimizador escolher uma varredura completa da tabela em vez de outros planos -- o otimizador na verdade conclui que não há nenhum outro plano possível a ser considerado.
Isso só é verdade para vários "construtores de linha" no lado direito de
IN
. Para uma única expressão, o otimizador faz seu trabalho e percebe que isso é equivalente acol_1 = a AND col_2 = b
:Curiosamente, seu original
EXPLAIN
sugere que o índice exclusivo não estava sendo usado exatamente da maneira que você pode ter acreditado que estava sendo usado, de qualquer forma. Ele estava sendo usado apenas para encontrar linhas com o sale_id desejado... não ambos os valores.Você notará em seu original
EXPLAIN
que okey_len
é mostrado como 4, o que significa que apenas os 4 bytes mais à esquerda do índice serão examinados -- sale_id, um 4 bytesINT
seriam os 4 bytes mais à esquerda nesse índice. Isso significa que oUsing where
otimizador percebe que a filtragem adicional das linhas retornadas da varredura de intervalo pode ser necessária para eliminar quaisquer linhas que não satisfaçam o restante daWHERE
cláusula -- todas as linhas com sale_id 502 e 504 estão sendo recuperadas por meio do índice, independentemente de seu valor para product_id e, em seguida, as linhas resultantes serão posteriormente filtradas para atender às restrições adicionais impostas peloWHERE
.O caminho ideal é provavelmente ficar com (expr e expr) ou (expr e expr) ou (expr e expr) em sua cláusula where. É logicamente equivalente e o otimizador o entende.
Nota adicional, em relação a alguns de seus comentários... à luz do que discuti acima, as dicas de índice não ajudarão, porque o otimizador parece não saber da equivalência da expressão que você usou com outras expressões que ele poderia manipular. .. mas como ponto de referência, o motivo pelo qual era sintaticamente inválido é que você precisa usar o nome do índice, não a lista de colunas no índice. Você chamou seu índice exclusivo de 'UNIQUE', então a maneira de usar isso como uma dica de índice seria neste formato:
Tente refatorar a consulta como um JOIN total
O índice deve ser utilizado neste caso.
Por que não usou o índice com sua consulta original? Eu culpo a cláusula WHERE porque o Query Optimizer viu a pesquisa sale_id primeiro e provavelmente decidiu que, com o restante da cláusula WHERE, uma verificação completa da tabela era o caminho de menor resistência.
De uma chance !!!