Mysql
 sql >> Database >  >> RDS >> Mysql

Perché una condizione IN dovrebbe essere più lenta di =in sql?

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;