2013-01-05 18 views
6

Poniżej znajduje się instrukcja SQL wewnątrz procedury przechowywanej (obcięty dla zwięzłości):MySQL WHERE NOT IN bardzo powolny

SELECT * 
FROM item a 
WHERE a.orderId NOT IN (SELECT orderId FROM table_excluded_item); 

Stwierdzenie to trwa 30 sekund lub tak! Ale jeśli usunę wewnętrzne zapytanie SELECT, spadnie do 1s. table_excluded_item nie jest ogromny, ale podejrzewam, że zapytanie wewnętrzne jest wykonywane bardziej niż musi.

Czy jest to skuteczniejszy sposób na zrobienie tego?

+1

Zapytanie wewnętrzne jest zależnym podzapytaniem, które jest znanym wąskim gardłem, ponieważ podzapytanie jest uruchamiane w każdym wierszu zewnętrznego zapytania. Sprawdź [Optymalizowanie podzapytań] (http://dev.mysql.com/doc/refman/5.1/en/optimizing-subqueries.html) na stronie internetowej MySQL. – Anthony

Odpowiedz

14

użycie LEFT JOIN

SELECT a.* 
FROM item a 
     LEFT JOIN table_excluded_item b 
      ON a.orderId = b.orderId 
WHERE b.orderId IS NULL 

upewnić, że orderId z obu tabel został indeksowane.

+0

Hej, to działa, dziękuję! Teraz jest 2s. Język zawsze wydaje mi się bardzo sprzeczny z intuicją :( – pixelfreak

+0

Nie ma za co ": D' –

1

Spróbuj tego i porównać do czasu LEFT JOIN zapytanie:

SELECT * 
FROM item a 
HAVING orderId NOT IN (SELECT orderId FROM table_excluded_item); 

ta jest mile widziana (używając HAVING gdy WHERE może być używany) od HAVING zakłada, że ​​warunek ograniczający (orderId) jest częścią wyniku zestaw. Ale myślę, że w tych scenariuszach ma to więcej sensu (ponieważ jest częścią zestawu wyników) i ponieważ jest jaśniejsze, co się dzieje, niż podejście LEFT JOIN.

Może być nieco wolniej, ale publikuj wyniki, aby wiedzieć, czy jest lepsza od pierwotnego zapytania.

+0

Wiwaty - na moim zestawie danych konsekwentnie mam ~ 7s dla tego vs ~ 8s dla lewego podejścia łączenia – hoju

5

Problem z podejściem po lewej stronie polega na tym, że duplikowane rekordy mogą być przetwarzane podczas generowania danych wyjściowych. Czasami tak nie jest. . . zgodnie z tym article, MySQL optymalizuje poprawnie left outer join, gdy kolumny są indeksowane, nawet w obecności duplikatów. Przyznaję jednak, że pozostaję sceptycznie nastawiony, że taka optymalizacja zawsze ma miejsce.

MySQL ma czasami problemy z optymalizacją instrukcji IN z podzapytaniem. Najlepszym fix jest skorelowane podzapytanie:

SELECT * 
FROM item a 
WHERE not exists (select 1 
        from table_excluded_item tei 
        where tei.orderid = a.orderid 
        limit 1 
       ) 

Jeśli indeks na table_excluded_item.orderid, to skanuje indeks i zatrzymują się na pierwszej wartości (the limit 1 nie może być absolutnie niezbędne do tego). Jest to najszybszy i najbezpieczniejszy sposób na wdrożenie tego, co chcesz w MySQL.

+2

Technicznie" limit 1 "to nie jest to konieczne, "anti-join" zrobi dokładnie to samo. (To * może być * tym, że mysql nie jest wystarczająco inteligentny, aby to wiedzieć) – wildplasser

+0

"Problem z podejściem lewego łączenia polega na tym, że można uzyskać duplikaty rekordów w wyjście. "- dlaczego? pamiętaj, że szukasz nieistniejącego rekordu –

+0

@jW ... Przeformułowałem to. –