2012-12-08 16 views
8

Stworzyłem 2 zapytania, które mogę użyć, które wykonują tę samą funkcję. Oba zawierają właściwości, które chciałbym scalić w jedno zapytanie, ale nie byłem w stanie.Optymalizacja indeksu MySQL z podzapytaniem a łączenie w lewo

QUERY 1 - Daje mi dokładnie takie wyniki, jakie chcę. Powolny (~ 0,700 s)

QUERY 2 - Daje mi wiele wierszy, które ignoruję i pomijam. Szybko (~ 0,005 s)

Moim celem jest zmodyfikowanie QUERY 2, aby usunąć wszystkie wiersze o zerowej cenie z wyjątkiem 1 dla każdej pozycji. Nie mogę tego zrobić, nie biorąc udziału w występie. Wynika to z mojego braku doświadczenia i zrozumienia użycia indeksu w MySQL.

ZAPYTANIE 1

Używa źle zaprojektowane podzapytanie, które nie zezwala na korzystanie z indeksowaniem całej tbl_sale (E), która zawiera 10k wierszy.

SELECT b.id, b.sv, b.description, der.store_id, f.name, der.price 
FROM tbl_watch AS a 
    LEFT JOIN tbl_item AS b ON a.item_id = b.id 
LEFT JOIN (
    SELECT c.store_id, d.flyer_id, e.item_id, e.price 
    FROM tbl_storewatch AS c, tbl_storeflyer AS d 
    FORCE INDEX (storebeg_ndx) , tbl_sale AS e 
    WHERE c.user_id = '$user_id' 
    AND (
     d.store_id = c.store_id 
     AND d.date_beg = '20121206' 
     ) 
    AND e.flyer_id = d.flyer_id 
     ) AS der ON a.item_id = der.item_id 
LEFT JOIN tbl_store as f ON der.store_id = f.id 
WHERE a.user_id = '$user_id' 
ORDER BY b.description ASC 

Oto EXPLAIN dla Zapytania 1

id select_type table  type possible_keys key    key_len  ref  rows Extra 
1 PRIMARY  a   ref  user_item_ndx user_item_ndx 4   const 30 Using index; Using temporary; Using filesort 
1 PRIMARY  b   eq_ref PRIMARY   PRIMARY   4   a.item_id 1 
1 PRIMARY  <derived2> ALL  NULL   NULL   NULL  NULL 300  
1 PRIMARY  f   eq_ref PRIMARY   PRIMARY   4   der.store_id 1 
2 DERIVED  c   ref  user_ndx  user_ndx  4     6 
2 DERIVED  e   ALL  NULL   NULL NULL NULL    9473 Using join buffer 
2 DERIVED  d   eq_ref storebeg_ndx storebeg_ndx 8   c.store_id 1 Using where 

DANYCH 2

wykorzystuje wszystkie lewej dołącza który jest bardzo skuteczny (z wyjątkiem ORDER BY). Indeksy są używane przy każdym sprzężeniu. Ta kwerenda zwraca wszystkie możliwe dopasowania dla każdego elementu w tbl_watch. Oto zapytanie:

SELECT b.id, b.sv, b.description, c.store_id, f.name, e.price 
FROM tbl_watch AS a 
LEFT JOIN tbl_item AS b ON a.item_id = b.id 
LEFT JOIN tbl_storewatch AS c ON c.user_id = '$user_id' 
LEFT JOIN tbl_storeflyer AS d ON d.store_id = c.store_id 
    AND d.date_beg = '$s_date' 
LEFT JOIN tbl_sale AS e ON e.item_id = a.item_id 
    AND e.flyer_id = d.flyer_id 
LEFT JOIN tbl_store as f ON d.store_id = f.id 
WHERE a.user_id = '$user_id' 
ORDER BY b.description ASC 

Oto EXPLAIN dla zapytania:

id select_type  table type possible_keys   key    key_len  ref      rows Extra 
1 SIMPLE   a  ref  user_item_ndx   user_item_ndx 4   const     6  Using index; Using temporary; Using filesort 
1 SIMPLE   b  eq_ref PRIMARY     PRIMARY   4   a.item_id    1 
1 SIMPLE   c  ref  user_ndx    user_ndx  4   const     2 
1 SIMPLE   d  eq_ref storebeg_ndx,storendx storebeg_ndx 8   c.store_id,const  1 
1 SIMPLE   e  eq_ref itemflyer_ndx   itemflyer_ndx 8   a.item_id,d.flyer_id 1 
1 SIMPLE   f  eq_ref PRIMARY     PRIMARY   4   d.store_id    1 

Jak mogę modyfikować QUERY 2 (bardziej wydajne), aby dać mi tylko wierszy muszę jak w kwerendzie 1 do pracować z?

Dzięki Mike

+0

Nie jestem pewien, jak to możliwe, że pierwsze zapytanie daje ci to, czego chcesz. Lewe sprzężenie nie jest lewe sprzężenie zewnętrzne (chociaż może jest w MySQL, nie jest zgodne z SQL), a wartości null nie są unikalnymi wartościami. Nie mam MySQL pod ręką, ale wprowadzenie go do PostgreSQL nie daje wyników, które opisujesz. Moja odpowiedź poniżej ... – PlexQ

Odpowiedz

0

Twój SUBSELECT w kwerendzie 1 używa niejawny sprzężenia wewnętrzne, podczas kwerendy 2 stosuje wszystko lewej dołącza wyraźny łączy. Tak więc, nie ma żadnych klauzul gdzie na wyłączenie danych w zapytaniu 2. zabrałbym się lewicy w kilku liniach (oznaczony) i zobaczyć, jak to poprawia rzeczy:

SELECT b.id, b.sv, b.description, c.store_id, f.name, e.price 
FROM tbl_watch AS a 
LEFT JOIN tbl_item AS b ON a.item_id = b.id 
LEFT JOIN tbl_storewatch AS c ON c.user_id = '$user_id' 
-- Left removed below 
JOIN tbl_storeflyer AS d ON d.store_id = c.store_id 
    AND d.date_beg = '$s_date' 
-- Left removed below 
JOIN tbl_sale AS e ON e.item_id = a.item_id 
    AND e.flyer_id = d.flyer_id 
LEFT JOIN tbl_store as f ON d.store_id = f.id 
WHERE a.user_id = '$user_id' 
ORDER BY b.description ASC` 

też może rozważyć biorąc i klauzule z łączeń i przenoszenie ich do WHERE:

i wreszcie matematyka z datą jest dość intensywna. W zapytaniu 2, używając zewnętrznych sprzężeń, unikasz go dużo, ale możesz go potrzebować. Chciałbym spróbować użyć podzapytania, aby uzyskać identyfikatory i ograniczyć przez to:

SELECT b.id, b.sv, b.description, c.store_id, f.name, e.price 
FROM tbl_watch AS a 
LEFT JOIN tbl_item AS b ON a.item_id = b.id 
LEFT JOIN tbl_storewatch AS c ON c.user_id = '$user_id' 
JOIN tbl_storeflyer AS d ON d.store_id = c.store_id 
JOIN tbl_sale AS e ON e.item_id = a.item_id 
LEFT JOIN tbl_store as f ON d.store_id = f.id 
WHERE a.user_id = '$user_id' 
AND e.flyer_id = d.flyer_id 
AND d.id in (select d.id from d where date_beg = '$s_date') 
ORDER BY b.description ASC 
+0

Dziękuję za odpowiedź! Rozwiązania te dają wiersze dla wszystkich elementów z aktywnymi elementami sprzedaży (e.item_id = a.item_id ORAZ e.flyer_id = d.flyer_id), ale próbowałem również uwzględnić każdą pozycję w tbl_watch (a) w polach (b), nawet jeśli nie istnieją w tbl_sale (e). W efekcie otrzymam: id, sv, description, NULL, NULL, NULL. Chciałem tylko pozwolić na 1 wiersz z wartościami NULL na element. Nie jestem pewien, jak to osiągnąć. – ridgeback

+0

Po to, by wyjaśnić, spodziewam się, że każdy przedmiot będzie przypominał 1 z 3 scenariuszy: 1 - Przedmiot z jedną ceną. 2 - Przedmiot z wieloma cenami. 3 - Przedmiot bez cen. Jeśli 3 wystąpi, nadal chcę zwrócić wiersz z identyfikatorem elementu, sv i opisem. – ridgeback

+0

Nie jestem pewien, czy date_beg tutaj jest rzeczywistą datą, wygląda na to, że jest używana jako znak. Nie jestem pewien, czy data jest powolna w MySQL, ale byłbym zaskoczony. Daty są zwykle przechowywane wewnętrznie jako długie, a jedynym kosztem jest zamienienie tego ciągu w długi, więc nie jestem przekonany, że w ogóle doda on jakiekolwiek obciążenie. – PlexQ

1

myślę ta kwerenda da Ci to, co chcesz:

select a.id, a.sv, a.description, c.id, c.name, b.price 
    from 
    tbl_item a left outer join tbl_sale b on (a.id=b.item_id) 
     left outer join tbl_storeflyer d on (b.flyer_id=d.flyer_id and d.date_beg = '20120801') 
     left outer join tbl_store c on (d.store_id = c.id) 
     left outer join tbl_storewatch x on (c.id = x.store_id) 
     left outer join tbl_watch y on (a.id = y.item_id); 

znakiem null zaangażowanych, jesteś prawdopodobnie będzie mieć kilka lewych złączeń.Alternatywny sposób jest użycie Unii, co z MySQL może być szybszy:

select a.id, a.sv, a.description, c.id as store_id, c.name, b.price 
    from 
    tbl_item a, 
    tbl_sale b, 
    tbl_storeflyer d, 
    tbl_store c, 
    tbl_storewatch x, 
    tbl_watch y 
    where 
    a.id = b.item_id and 
    b.flyer_id = d.flyer_id and 
    d.store_id = c.id and 
    c.id = x.store_id and 
    a.id = y.item_id and 
    d.date_beg = '20120801' 
union 
select a.id, a.sv, a.description, null as store_id, null as name, null as price 
    from 
    tbl_item a 
    where 
    a.id not in (select b.item_id from tbl_sale b); 

może grać z drugiej połowy unii bycia lewe sprzężenie zewnętrzne zamiast „nie” podzapytaniu - zależy od tego, jak twoja wersja MySQL jest optymalizowana.

Powiązane problemy