Samenvatting:dit is een bekend probleem in MySQL en is opgelost in MySQL 5.6.x. Het probleem is te wijten aan een ontbrekende optimalisatie wanneer een subquery met IN ten onrechte wordt geïdentificeerd als afhankelijke subquery in plaats van een onafhankelijke subquery.
Wanneer u EXPLAIN uitvoert op de oorspronkelijke query, wordt dit geretourneerd:
1 'PRIMARY' 'question_law_version' 'ALL' '' '' '' '' 10148 'Using where' 2 'DEPENDENT SUBQUERY' 'question_law_version' 'ALL' '' '' '' '' 10148 'Using where' 3 'DEPENDENT SUBQUERY' 'question_law' 'ALL' '' '' '' '' 10040 'Using where'
Wanneer u IN
verandert naar =
je krijgt dit:
1 'PRIMARY' 'question_law_version' 'ALL' '' '' '' '' 10148 'Using where' 2 'SUBQUERY' 'question_law_version' 'ALL' '' '' '' '' 10148 'Using where' 3 'SUBQUERY' 'question_law' 'ALL' '' '' '' '' 10040 'Using where'
Elke afhankelijke subquery wordt eenmaal per rij uitgevoerd in de query waarin deze zich bevindt, terwijl de subquery slechts eenmaal wordt uitgevoerd. MySQL kan soms afhankelijke subquery's optimaliseren wanneer er een voorwaarde is die kan worden geconverteerd naar een join, maar hier is dat niet het geval.
Dit laat natuurlijk de vraag open waarom MySQL van mening is dat de IN-versie een afhankelijke subquery moet zijn. Ik heb een vereenvoudigde versie van de query gemaakt om dit te helpen onderzoeken. Ik heb twee tabellen 'foo' en 'bar' gemaakt, waarbij de eerste alleen een id-kolom bevat en de laatste zowel een id als een foo-id (hoewel ik geen beperking voor een externe sleutel heb gemaakt). Vervolgens vulde ik beide tabellen met 1000 rijen:
CREATE TABLE foo (id INT PRIMARY KEY NOT NULL);
CREATE TABLE bar (id INT PRIMARY KEY, foo_id INT NOT NULL);
-- populate tables with 1000 rows in each
SELECT id
FROM foo
WHERE id IN
(
SELECT MAX(foo_id)
FROM bar
);
Deze vereenvoudigde query heeft hetzelfde probleem als voorheen:de inner select wordt behandeld als een afhankelijke subquery en er wordt geen optimalisatie uitgevoerd, waardoor de inner query eenmaal per rij wordt uitgevoerd. Het uitvoeren van de query duurt bijna een seconde. De IN
wijzigen naar =
zorgt ervoor dat de zoekopdracht bijna onmiddellijk kan worden uitgevoerd.
De code die ik heb gebruikt om de tabellen te vullen staat hieronder, voor het geval iemand de resultaten wil reproduceren.
CREATE TABLE filler (
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT
) ENGINE=Memory;
DELIMITER $$
CREATE PROCEDURE prc_filler(cnt INT)
BEGIN
DECLARE _cnt INT;
SET _cnt = 1;
WHILE _cnt <= cnt DO
INSERT
INTO filler
SELECT _cnt;
SET _cnt = _cnt + 1;
END WHILE;
END
$$
DELIMITER ;
CALL prc_filler(1000);
INSERT foo SELECT id FROM filler;
INSERT bar SELECT id, id FROM filler;