sql >> Base de Datos >  >> RDS >> Mysql

¿Por qué una condición IN sería más lenta que =en sql?

Resumen:Este es un problema conocido en MySQL y se corrigió en MySQL 5.6.x. El problema se debe a una falta de optimización cuando una subconsulta que usa IN se identifica incorrectamente como una subconsulta dependiente en lugar de una subconsulta independiente.

Cuando ejecuta EXPLAIN en la consulta original, devuelve esto:

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'

Cuando cambias IN a = Obtienes esto:

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'

Cada subconsulta dependiente se ejecuta una vez por fila en la consulta en la que está contenida, mientras que la subconsulta se ejecuta solo una vez. MySQL a veces puede optimizar las subconsultas dependientes cuando hay una condición que se puede convertir en una combinación, pero aquí ese no es el caso.

Ahora, esto, por supuesto, deja la pregunta de por qué MySQL cree que la versión IN debe ser una subconsulta dependiente. He hecho una versión simplificada de la consulta para ayudar a investigar esto. Creé dos tablas 'foo' y 'bar' donde la primera contiene solo una columna de identificación, y la última contiene una identificación y una identificación de foo (aunque no creé una restricción de clave externa). Luego llené ambas tablas con 1000 filas:

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
);

Esta consulta simplificada tiene el mismo problema que antes:la selección interna se trata como una subconsulta dependiente y no se realiza ninguna optimización, lo que hace que la consulta interna se ejecute una vez por fila. La consulta tarda casi un segundo en ejecutarse. Cambiando el IN a = nuevamente permite que la consulta se ejecute casi instantáneamente.

El código que utilicé para completar las tablas se encuentra a continuación, en caso de que alguien desee reproducir los resultados.

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;