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

Hvorfor ville en IN-tilstand være langsommere end =i sql?

Resumé:Dette er et kendt problem i MySQL og blev rettet i MySQL 5.6.x. Problemet skyldes en manglende optimering, når en underforespørgsel, der bruger IN, er forkert identificeret som afhængig underforespørgsel i stedet for en uafhængig underforespørgsel.

Når du kører EXPLAIN på den oprindelige forespørgsel, returnerer den dette:

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'

Når du ændrer IN til = du får dette:

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'

Hver afhængig underforespørgsel køres én gang pr. række i den forespørgsel, den er indeholdt i, hvorimod underforespørgslen kun køres én gang. MySQL kan nogle gange optimere afhængige underforespørgsler, når der er en betingelse, der kan konverteres til en joinforbindelse, men det er ikke tilfældet her.

Nu efterlader dette naturligvis spørgsmålet om, hvorfor MySQL mener, at IN-versionen skal være en afhængig underforespørgsel. Jeg har lavet en forenklet version af forespørgslen for at hjælpe med at undersøge dette. Jeg oprettede to tabeller 'foo' og 'bar', hvor førstnævnte kun indeholder en id-kolonne, og sidstnævnte indeholder både et id og et foo-id (selvom jeg ikke oprettede en fremmednøglebegrænsning). Så udfyldte jeg begge tabeller med 1000 rækker:

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

Denne forenklede forespørgsel har samme problem som før - det indre udvalg behandles som en afhængig underforespørgsel, og der udføres ingen optimering, hvilket medfører, at den indre forespørgsel køres én gang pr. række. Forespørgslen tager næsten et sekund at køre. Ændring af IN til = igen tillader forespørgslen at køre næsten øjeblikkeligt.

Den kode, jeg brugte til at udfylde tabellerne, er nedenfor, hvis nogen ønsker at gengive resultaterne.

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;


  1. Sådan genereres scripts for at føje standardbegrænsninger til kolonne i flere tabeller i SQL Server-database - SQL Server / TSQL vejledning del 94

  2. Sådan opretter du en bruger med PSQL

  3. Sådan tjekker du gammel statistik

  4. AT TIME ZONE – en ny favoritfunktion i SQL Server 2016