Mysql
 sql >> Baza danych >  >> RDS >> Mysql

Dlaczego warunek IN miałby być wolniejszy niż =w sql?

Podsumowanie:jest to znany problem w MySQL i został naprawiony w MySQL 5.6.x. Problem jest spowodowany brakiem optymalizacji, gdy podzapytanie używające IN jest nieprawidłowo identyfikowane jako zależne podzapytanie zamiast niezależnego podzapytania.

Po uruchomieniu polecenia EXPLAIN w pierwotnym zapytaniu zwraca ono następujące polecenie:

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'

Po zmianie IN do = otrzymujesz to:

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żde zależne podzapytanie jest uruchamiane raz na wiersz w zapytaniu, w którym się znajduje, podczas gdy podzapytanie jest uruchamiane tylko raz. MySQL może czasami optymalizować zależne podzapytania, gdy istnieje warunek, który można przekonwertować na łączenie, ale w tym przypadku tak nie jest.

To oczywiście pozostawia pytanie, dlaczego MySQL uważa, że ​​wersja IN musi być podzapytaniem zależnym. Zrobiłem uproszczoną wersję zapytania, aby pomóc to zbadać. Utworzyłem dwie tabele „foo” i „bar”, gdzie pierwsza zawiera tylko kolumnę id, a druga zawiera zarówno identyfikator, jak i identyfikator foo (chociaż nie stworzyłem ograniczenia klucza obcego). Następnie wypełniłem obie tabele 1000 wierszami:

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

To uproszczone zapytanie ma ten sam problem, co poprzednio — wewnętrzna selekcja jest traktowana jako zależne podzapytanie i nie jest przeprowadzana żadna optymalizacja, co powoduje, że wewnętrzne zapytanie jest uruchamiane raz na wiersz. Wykonanie zapytania zajmuje prawie sekundę. Zmiana IN do = ponownie umożliwia niemal natychmiastowe uruchomienie zapytania.

Kod, którego użyłem do wypełnienia tabel, znajduje się poniżej, na wypadek gdyby ktoś chciał odtworzyć wyniki.

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. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Jak dodać domyślne ograniczenie w MySQL?

  2. Tworzenie funkcji w phpMyAdmin - Błąd:odmowa dostępu, potrzebujesz super uprawnień do tej operacji

  3. Jak pobrać pierwszy i ostatni rekord rekordu zgrupowanego w zapytaniu MySQL za pomocą funkcji agregujących?

  4. Kategorie rekurencyjne z jednym zapytaniem?

  5. 2 sposoby na wyświetlenie wszystkich procedur składowanych w MySQL