Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Warum wäre eine IN-Bedingung langsamer als =in SQL?

Zusammenfassung:Dies ist ein bekanntes Problem in MySQL und wurde in MySQL 5.6.x behoben. Das Problem ist auf eine fehlende Optimierung zurückzuführen, wenn eine Unterabfrage, die IN verwendet, fälschlicherweise als abhängige Unterabfrage statt als unabhängige Unterabfrage identifiziert wird.

Wenn Sie EXPLAIN für die ursprüngliche Abfrage ausführen, wird Folgendes zurückgegeben:

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'

Wenn Sie IN ändern zu = Sie erhalten dies:

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'

Jede abhängige Unterabfrage wird einmal pro Zeile in der Abfrage ausgeführt, in der sie enthalten ist, während die Unterabfrage nur einmal ausgeführt wird. MySQL kann manchmal abhängige Unterabfragen optimieren, wenn es eine Bedingung gibt, die in einen Join umgewandelt werden kann, aber hier ist das nicht der Fall.

Damit bleibt natürlich die Frage, warum MySQL glaubt, dass die IN-Version eine abhängige Unterabfrage sein muss. Ich habe eine vereinfachte Version der Abfrage erstellt, um dies zu untersuchen. Ich habe zwei Tabellen „foo“ und „bar“ erstellt, wobei die erstere nur eine ID-Spalte enthält und die letztere sowohl eine ID als auch eine foo-ID enthält (obwohl ich keine Fremdschlüsseleinschränkung erstellt habe). Dann habe ich beide Tabellen mit 1000 Zeilen gefüllt:

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

Diese vereinfachte Abfrage hat das gleiche Problem wie zuvor – die innere Auswahl wird als abhängige Unterabfrage behandelt und es wird keine Optimierung durchgeführt, wodurch die innere Abfrage einmal pro Zeile ausgeführt wird. Die Ausführung der Abfrage dauert fast eine Sekunde. Änderung des IN zu = ermöglicht wiederum, dass die Abfrage fast sofort ausgeführt wird.

Der Code, den ich zum Füllen der Tabellen verwendet habe, ist unten, falls jemand die Ergebnisse reproduzieren möchte.

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;