I have a primary with an dependant subquery as a where condition that is choosing a wrong execution plan, and therefore is running for ever.
Main query:
SELECT rtr.id AS id156_,
rtr.procesada AS procesada156_,
rtr.idTransaccion AS idTransa3_156_
FROM
ReservasTiempoReal rtr
INNER JOIN Transacciones t ON t.id=rtr.idTransaccion
INNER JOIN TransaccionLineas tl ON tl.idTransaccion = rtr.idTransaccion
INNER JOIN Productos prod ON prod.id=tl.idProducto
INNER JOIN Proveedores prov ON prov.id=prod.idProveedor
WHERE t.confirmada = 'S'
AND t.codTipoTransaccion = 'RTRCT'
AND t.fechaHora >= '2015-01-20 15:30:59'
AND t.fechaHora <= '2015-01-21 15:30:59'
AND prov.id=77
AND (EXISTS ( SELECT rtr2.id
FROM ReservasTiempoReal rtr2
INNER JOIN Transacciones t2 ON rtr2.idTransaccion = t2.id
WHERE t2.confirmada = 'S'
AND t2.codTipoTransaccion = 'ECTR'
AND t2.idTransaccionOriginal = rtr.idTransaccion));
And its execution plan:
In red is shown the used index using ‘codTipoTransaccion’ and ‘confirmada’ columns.
The first query (without the inner one) returns only 10 rows. But the whole transaction table has over 5 millon records. And most of them have the attribute confirmada = ‘S’.
If I delete the index ‘idxConfirmada’ (index by ‘confirmada’ field) the query takes only 10 seconds to execute and the execution plan is:
Just to add more info, if I execute the dependent inner query only with the records identified in the primary query, it returns only 6 records. The used query was:
SELECT rtr2.id
FROM ReservasTiempoReal rtr2
INNER JOIN Transacciones t2 ON rtr2.idTransaccion = t2.id
WHERE t2.confirmada = 'S'
AND t2.codTipoTransaccion = 'ECTR'
AND t2.idTransaccionOriginal IN (14748319,
14748519,14750327,14751060,14751126,14751351,14751354,14752902,14754752,
14754785)
I don’t want to delete the ‘idxConfirmada’ index, I need the ORM to choose the correct plan. Also, I cannot use hints to ignore the index as the query is coming from hibernate.
How can I make mysql to choose the correct plan (not using idxConfirmada) ?
I am using MYSQL 5.1.51.
Added table details
CREATE TABLE `Transacciones` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`fechaHora` datetime NOT NULL ,
`nroTicket` varchar(255) NOT NULL ,
`puntosGenerados` decimal(19,2) DEFAULT NULL,
`creditoPendiente` decimal(19,2) DEFAULT NULL,
`debitoPendiente` decimal(19,2) DEFAULT NULL,
`saldoReal` decimal(19,2) DEFAULT NULL,
`idTransaccionOriginal` bigint(20) DEFAULT NULL,
`codTipoTransaccion` varchar(5) DEFAULT NULL,
`confirmada` varchar(1) NOT NULL,
`nroLote` varchar(5) DEFAULT NULL,
`idMonedaImporteFinal` bigint(20) DEFAULT NULL,
`totalImporteFinal` decimal(19,2) DEFAULT NULL,
`anulada` varchar(1) DEFAULT NULL,
`idLote` bigint(20) DEFAULT NULL,
`online` varchar(1) DEFAULT NULL,
`manualConPIN` varchar(1) DEFAULT NULL,
`manualSinPIN` varchar(1) DEFAULT NULL,
`facturada` varchar(1) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `FK2D546D5D771F9D3A` (`idTransaccionOriginal`),
KEY `idxAnulada` (`anulada`),
KEY `idxConfirmada` (`confirmada`),
KEY `idxCodTipoTransaccion` (`codTipoTransaccion`),
KEY `idxFechaHora_CodTipoTransaccion` (`fechaHora`,`codTipoTransaccion`),
KEY `idxNroTicket` (`nroTicket`),
KEY `idxNroLote` (`nroLote`),
KEY `idxFechaHora` (`fechaHora`),
KEY `idxOnline` (`online`),
KEY `idxFacturada` (`facturada`),
CONSTRAINT `FK2D546D5D771F9D3A` FOREIGN KEY (`idTransaccionOriginal`) REFERENCES `Transacciones` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=15359563 DEFAULT CHARSET=latin1
CREATE TABLE `ReservasTiempoReal` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`procesada` varchar(1) NOT NULL,
`idTransaccion` bigint(20) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `idTransaccion` (`idTransaccion`),
KEY `FK2375B195C078E769` (`idTransaccion`),
KEY `idxProcesada` (`procesada`),
CONSTRAINT `FK2375B195C078E769` FOREIGN KEY (`idTransaccion`) REFERENCES `Transacciones` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=50805 DEFAULT CHARSET=latin1
Best Answer
Use a composite index:
The columns can be in any order, so I recommend shuffling them to meet other need(s) and/or facilitate removing some other index from
t2
.If you need to discuss this further, please provide
SHOW CREATE TABLE
for each table.Edit
For
it is probably better to have this:
with
idTransaccionOriginal
deliberately last (because of theIN
).That should encourage the optimizer to start with
t2
. I see thatidTransaccion
is an index in the other table, so theJOIN
should be efficient. (On a cold cache, there could still be a lot of I/O.)Your
CREATE TABLE
multiple not-so-good indexes for the optimizer (not the ORM) to choose from. What I am proposing should be a 'better' index that the optimizer will readily pick. The optimizer looks at all the indexes, ignores any that are obviously useless (for the given query), and looks at statistics. In some situations (perhaps not yours), the statistics are poor, leading to a poor choice ofINDEX
. The "statistics" for one index is one number; it assumes a balanced distribution (which you point out is wrong forconfirmada='S'
).Using a composite index (when it can be used) is almost always better than having shorter indexes.
(Unrelated)
DROP
the second of these, as being redundant:Also, my suggested index would make
KEY idxCodTipoTransaccion (codTipoTransaccion)
unnecessary.More discussion on indexing.