Riepilogo:questo è un problema noto in MySQL ed è stato corretto in MySQL 5.6.x. Il problema è dovuto a un'ottimizzazione mancante quando una sottoquery che utilizza IN viene erroneamente identificata come subquery dipendente anziché come subquery indipendente.
Quando esegui EXPLAIN sulla query originale, restituisce questo:
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'
Quando modifichi IN
a =
ottieni questo:
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'
Ciascuna sottoquery dipendente viene eseguita una volta per riga nella query in cui è contenuta, mentre la sottoquery viene eseguita una sola volta. MySQL a volte può ottimizzare le sottoquery dipendenti quando esiste una condizione che può essere convertita in un join, ma qui non è così.
Ora questo ovviamente lascia la domanda sul perché MySQL crede che la versione IN debba essere una subquery dipendente. Ho creato una versione semplificata della query per aiutare a indagare su questo. Ho creato due tabelle "foo" e "bar" in cui la prima contiene solo una colonna id e la seconda contiene sia un id che un foo id (sebbene non abbia creato un vincolo di chiave esterna). Quindi ho popolato entrambe le tabelle con 1000 righe:
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
);
Questa query semplificata presenta lo stesso problema di prima:la selezione interna viene trattata come una sottoquery dipendente e non viene eseguita alcuna ottimizzazione, causando l'esecuzione della query interna una volta per riga. La query impiega quasi un secondo per essere eseguita. Modifica del IN
a =
consente di nuovo di eseguire la query quasi istantaneamente.
Il codice che ho usato per popolare le tabelle è di seguito, nel caso qualcuno volesse riprodurre i risultati.
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;