sql >> Databáze >  >> RDS >> Mysql

Proč by podmínka IN byla pomalejší než =v sql?

Shrnutí:Toto je známý problém v MySQL a byl opraven v MySQL 5.6.x. Problém je způsoben chybějící optimalizací, kdy je poddotaz používající IN nesprávně označen jako závislý poddotaz namísto nezávislého poddotazu.

Když spustíte EXPLAIN na původní dotaz, vrátí toto:

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'

Když změníte IN na = dostanete toto:

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'

Každý závislý poddotaz je spuštěn jednou na řádek v dotazu, ve kterém je obsažen, zatímco poddotaz je spuštěn pouze jednou. MySQL může někdy optimalizovat závislé poddotazy, když existuje podmínka, kterou lze převést na spojení, ale v tomto případě tomu tak není.

Nyní to samozřejmě ponechává otázku, proč se MySQL domnívá, že verze IN musí být závislým poddotazem. Vytvořil jsem zjednodušenou verzi dotazu, která to pomůže prozkoumat. Vytvořil jsem dvě tabulky 'foo' a 'bar', kde první obsahuje pouze sloupec id a druhá obsahuje id i foo id (ačkoli jsem nevytvořil omezení cizího klíče). Poté jsem naplnil obě tabulky 1000 řádky:

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

Tento zjednodušený dotaz má stejný problém jako dříve – s vnitřním výběrem se zachází jako se závislým poddotazem a neprovádí se žádná optimalizace, což způsobí, že se vnitřní dotaz spustí jednou na řádek. Spuštění dotazu trvá téměř jednu sekundu. Změna IN na = opět umožňuje spuštění dotazu téměř okamžitě.

Kód, který jsem použil k naplnění tabulek, je níže pro případ, že by si někdo přál výsledky reprodukovat.

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. Databáze Python a MySQL:Praktický úvod

  2. Existuje / neexistuje:'vybrat 1' vs 'vybrat pole'

  3. Driver.getConnection se zablokuje pomocí ovladače SQLServer a Java 1.6.0_29

  4. Trailing Zero