2011-08-09 13 views
6

Pracuję nad generowaniem raportów dla danych zawartych w dużej wcześniej istniejącej bazie danych programu Access (~ 500 MB po kompaktowej naprawie &) i mam problem z wolnym podzapytaniem .Bardzo wolne podzapytania przy korzystaniu z "NOT IN"

Baza danych zawiera dużą tabelę, która zawiera zapis każdego zakupu klienta. Oto prosta kwerenda, która znajduje klientów, którzy kupili niebieski widget. Kończy się w ciągu kilku sekund i zwraca około dziesięciu tysięcy rekordów.

SELECT DISTINCT CustomerId 
FROM ProductSales 
WHERE Product = 'BLUE' 

Oto zapytanie, które próbuje znaleźć klientów, którzy kupili niebieski widget, ale nie czerwony widget. Uruchomienie trwa około godziny.

SELECT DISTINCT CustomerId FROM ProductSales 
WHERE Product = 'BLUE' 
AND CustomerId NOT IN (
    SELECT CustomerId 
    FROM ProductSales 
    WHERE Product = 'RED' 
) 

Czy istnieje sposób na zreorganizowanie drugiego zapytania, aby potrwać kilka minut zamiast godziny?

+0

Zakładam, że pole CustomerId zawiera indeksy w obu tabelach? –

+0

Czy próbowałeś WYBIERZ ODLEGŁOŚĆ CustomerId FROM ProductSales WHERE Product = 'BLUE' minus WYBIERZ CustomerId FROM ProductSales WHERE Product = 'RED'. Widziałem przypadki, w których naprawdę przyspieszyło to zapytanie, ale YMMV –

+0

@Marc B: Tutaj jest tylko jedna tabela, ale CustomerId jest na niej indeksowany. – James

Odpowiedz

10

dostępu silnik bazy danych nie można używać indeksu dla Not In, więc to wiąże się powoli. Z indeksem na CustomerId, to zapytanie powinno być znacznie szybsze, ponieważ silnik bazy danych może korzystać z indeksu.

SELECT DISTINCT blue.CustomerId 
FROM 
    ProductSales AS blue 
    LEFT JOIN 
     (
      SELECT CustomerId 
      FROM ProductSales 
      WHERE Product = 'RED' 
     ) AS red 
    ON blue.CustomerId = red.CustomerId 
WHERE 
     blue.Product = 'BLUE' 
    AND red.CustomerId Is Null; 

prawdopodobnie można także spróbować podejścia Not Exists, ale użycie indeksu nie jest gwarantowana. Zobacz także komentarz Davida Fentona poniżej, w którym omówiono wpływ na wydajność bardziej szczegółowo.

+1

Woah. W dół od 60 minut do około sekundy. To około 3600 razy szybciej niż w starej wersji. Dzięki! :-) Na przyszłość, czy istnieje lista operacji SQL Access nie używa gdzieś indeksu? – James

+1

Prawdopodobnie jest gdzieś lista, ale nie wiem gdzie. :-) Jeśli chcesz na tym polegać, Google Jet ShowPlan ... powie ci autorytatywnie, jak/jeśli silnik bazy danych korzysta z indeksów z zapytaniem. Szczegółowe omówienie wydajności zapytań można znaleźć tutaj: http://msdn.microsoft.com/en-us/library/aa188211(office.10).aspx – HansUp

+1

Nie oznacza to, że NIE JESTEŚ I nigdy nie korzysta z indeksów - to jest że nie można przewidzieć, kiedy będą, a kiedy nie. Za każdym razem, gdy możesz ponownie zaprojektować podzapytanie NOT IN/EXISTS w JOIN, prawdopodobnie poprawisz wydajność. Wszystko zależy jednak od możliwości edycji wynikowego zestawu rekordów - niektóre podzapytania użyte w JOIN sprawią, że zapytanie nie będzie możliwe do aktualizacji. –

0

Dodaj indeks, oczywiście, jeśli go nie masz. Jeśli to jest problem, to prawdopodobnie tylko dlatego, że jest wielu klientów z zamówieniami na coś innego niż RED, ale nie tak wielu z BLUE; to (nieprzetestowane) zapytanie próbuje to naprawić.

SELECT DISTINCT CustomerId FROM ProductSales 
LEFT JOIN (
    SELECT DISTINCT CustomerId cid FROM ProductSales 
    LEFT JOIN (
    SELECT DISTINCT CustomerId 
    FROM ProductSales 
    WHERE Product = 'BLUE' 
) foo ON CustomerId = cid 
    WHERE Product = 'RED' 
) bar USING (CustomerId) 
WHERE cid IS NULL 
Powiązane problemy