2010-09-30 14 views
7

od dłuższego czasu lurker, pierwsze pytanie!Powolne zapytanie Mysql: JOIN + wiele GDZIE + ZAMÓWIENIE PRZEZ

walczę, aby zoptymalizować tę kwerendę, która wybiera i najniższej cenie elementy, które spełniają wybrane filtry:

SELECT product_info.*, MIN(product_all.sale_price) as sale_price, product_all.buy_link 
FROM product_info 
NATURAL JOIN (SELECT * FROM product_all WHERE product_all.date = '2010-09-30') as product_all 
WHERE (product_info.category = 2 
AND product_info.gender = 'W') 
GROUP BY product_all.prod_id 
ORDER BY MIN(product_all.sale_price) ASC LIMIT 13 

Jego wyjaśnić:

| id | select_type | table  | type | possible_keys            | key  | key_len | ref     | rows | Extra       | 
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 
| 1 | PRIMARY  | <derived2> | ALL | NULL              | NULL | NULL | NULL    | 89801 | Using temporary; Using filesort | 
| 1 | PRIMARY  | product_info | eq_ref | PRIMARY,category_prod_id_retail_price,category_ret...  | PRIMARY | 4  | product_all.prod_id | 1  | Using where      | 
| 2 | DERIVED  | product_all | ref | date_2             | date_2 | 3  |      | 144107 |         | 

Próbowałem eliminując podzapytanie, które intuicyjnie wydaje się lepszy, ale w praktyce trwa jeszcze dłużej:

SELECT product_info.*, MIN(product_all.sale_price) as sale_price, product_all.buy_link 
FROM product_info 
NATURAL JOIN product_all 
WHERE (product_all.date = '2010-09-30' 
AND product_info.category = 2 
AND product_info.gender = 'W') 
GROUP BY product_all.prod_id 
ORDER BY MIN(product_all.sale_price) ASC LIMIT 13 

I jego exp lain:

| id | select_type | table  | type | possible_keys            | key      | key_len | ref        | rows | Extra          | 
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 
| 1 | SIMPLE  | product_info | ref | PRIMARY,category_prod_id_retail_price,category_ret...  | category_retail_price | 5  | const        | 269 | Using where; Using temporary; Using filesort | 
| 1 | SIMPLE  | product_all | ref | PRIMARY,prod_id,date_2         | prod_id     | 4  | equipster_db.product_info.prod_id | 141 | Using where         | 

Oto tabele:

CREATE TABLE `product_all` (
`prod_id` INT(10) NOT NULL PRIMARY KEY , 
`ref_id` INT(10) NOT NULL PRIMARY KEY , 
`date` DATE NOT NULL , 
`buy_link` BLOB NOT NULL , 
`sale_price` FLOAT NOT NULL 
) ENGINE = MYISAM ; 


CREATE TABLE `product_info` (
`prod_id` INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY , 
`prod_name` VARCHAR(200) NOT NULL, 
`brand` VARCHAR(50) NOT NULL, 
`retail_price` FLOAT NOT NULL 
`category` INT(3) NOT NULL, 
`gender` VARCHAR(1) NOT NULL, 
`type` VARCHAR(10) NOT NULL 
) ENGINE = MYISAM ; 

moje pytania: struktura
-co wydaje się optymalne zapytania?
-które wskaźniki zoptymalizowałyby to zapytanie?
nie- ważniejsze: Jak zmienia się podejście indeksowania podczas dodawania lub usuwania WHERE lub przy użyciu innego ORDER BY, takich jak sortując% off:

ORDER BY (1-(MIN(product_all.sale_price)/product_info.retail_price)) DESC 

edit: naturalne działa zarówno na Dołącz do zapytań prod_id (jeden rekord w product_info może mieć wiele instancji w product_all, dlatego należy je zgrupować)

+0

jeden z PK jest złożony, ale tak, każda grupa to jeden wiersz: identyfikator produktu, najniższa cena dla tego produktu i powiązane dane. edytuj: była to odpowiedź na komentarz, który prawdopodobnie zniknął. edit2: tak, myślę, że kliknę edytuj zamiast dodawać komentarze ... gładko. – chrisblanch

Odpowiedz

4

Indeksy robią ogromną różnicę w mysql, jedno zapytanie, które zajęło 15 minut z błędnym zestawem indeksów zajęło 0,2 sekundy z właściwymi, ale znalezienie właściwej równowagi to zazwyczaj problem. Naturalnie bez przykładowych danych trudno powiedzieć, czy poniższe rozwiązanie zaoszczędzi w dowolnym momencie, ale teoretycznie powinno.

Aby odpowiedzieć na Twoje pytania, chciałbym przeprojektować tabele tak:

CREATE TABLE `product_all` ( 
`prod_id` INT(10) NOT NULL, 
`ref_id` INT(10) NOT NULL, 
`date` DATE NOT NULL , 
`buy_link` BLOB NOT NULL , 
`sale_price` FLOAT NOT NULL, 
PRIMARY KEY (prod_id, ref_id) , 
INDEX date_Index (`date` ASC), 
UNIQUE INDEX prod_price_Index (prod_id ASC, sale_price ASC) 
) ENGINE = MYISAM ; 


CREATE TABLE `product_info` ( 
`prod_id` INT(10) NOT NULL AUTO_INCREMENT, 
`prod_name` VARCHAR(200) NOT NULL, 
`brand` VARCHAR(50) NOT NULL, 
`retail_price` FLOAT NOT NULL, 
`category` INT(3) NOT NULL, 
`gender` VARCHAR(1) NOT NULL, 
`type` VARCHAR(10) NOT NULL, 
PRIMARY KEY (prod_id) , 
UNIQUE INDEX prod_id_name_Index (prod_id ASC, prod_name ASC), 
INDEX category_Index (category ASC), 
INDEX gender_Index (gender ASC) 
) ENGINE = MYISAM ; 

SELECT product_info.*, MIN(product_all.sale_price) as sale_price, product_all.buy_link   
FROM product_info   
NATURAL JOIN (SELECT * FROM product_all WHERE product_all.date = '2010-09-30') as product_all   
WHERE (product_info.category = 2   
AND product_info.gender = 'W')   
GROUP BY product_all.prod_id   
ORDER BY MIN(product_all.sale_price) ASC LIMIT 13   

Przyrost wydajności jest tu zyskał moją indeksowanie główne dziedziny, które są połączone na i biorące udział w klauzuli WHERE. Osobiście chciałbym pójść z pierwszym zapytaniem, gdy myślisz o tym, że powinno działać lepiej.

O ile mi zrozumieć, co dzieje się w pierwszym i drugim zapytaniu:

  • Pierwsze zapytanie jest filtrowane przez sub-kwerendy przed robi naturalny przyłączyć, co oznacza, że ​​jej tylko przystąpienie do w wynikowych danych, a nie w całej tabeli.
  • Druga kwerenda dołącza do całej drugiej tabeli , a następnie filtruje wynikowe wiersze całego zbioru z powrotem do żądanych.

Zgodnie z ogólną zasadą zwykle należy dodawać indeksy na głównych polach łączenia, a także w polach, w których najczęściej używane są klauzule where.Umieściłem także kilka unikalnych indeksów na niektórych polach, które będą regularnie wyszukiwane, takie jak prod_id_name_Index.

Jeśli to nie poprawia wydajności, jeśli możesz opublikować jakieś fałszywe dane do gry, być może uda mi się uzyskać szybsze rozwiązanie, które można porównać.

Here to artykuł, który przechodzi indeksowanie wydajności w mysql, warto przeczytać, jeśli chcesz wiedzieć więcej.

Powodzenia!

EDYCJA: Twoje ostatnie pytanie, za którym tęskniłem za pierwszym razem, odpowiedź brzmi, że jeśli indeksowanie głównych pól łączenia zmieni się w miejsce, które tylko nieznacznie wpłynie na ogólną wydajność, ale unikalne indeksy, które umieściłem na tabelach powinien uwzględniać większość rzeczy, na które chcesz bazować. Najważniejszą rzeczą do zapamiętania jest to, że często pytasz lub przyłączasz się do pola, wtedy naprawdę powinno ono być indeksowane, ale drobne zapytania i zmiany w porządku przez ciebie nie powinny się martwić, jeśli chodzi o wyrównywanie strategii indeksowania.

+0

jon, dzięki! te wielokrotne indeksy kolumn zrobiły lewę, a także twoja edycja była na miejscu, kolejność przez naprawdę nie przeciągała zapytania, ponieważ działa tylko w 13 rzędach. Twoje zdrowie! – chrisblanch

+0

Jon, pomógł nam się świetnie. Ten kawałek na indeksie JOIN jest czymś, o czym wcześniej nie słyszałem i był to ratownik na podobne problemy. – jerebear

+0

Zawsze dobrze słyszeć! Jest to często wyglądająca część projektu bazy danych, która może czasami drogo cię kosztować, ciesząc się z pomocy. – JonVD

0

Wydajność mądry, jego nigdy nie jest dobrą rzeczą, aby korzystać

select * 

Zamiast tego należy użyć poszczególne nazwy kolumn.

select column1,column2 etc... 
+0

słowo do tego ... jedna z niewielu rzeczy, które znałem, ale uznałem, że to pomijalne i poprawia czytelność mojego pytania. – chrisblanch

0

Osobiście jestem sql minimalistyczny i unikać wszelkiego rodzaju sub zapytań lub dołącza które nie mogą być wskaźnikiem do kolumn indeksu.

Jeśli to nie jest możliwe, prawdopodobnie będę uruchamiać podkwerendy pojedynczo, aby zebrać klucze, posortować ich stronę klienta, a następnie zbudować klauzulę, w której (...).

JohnVD ma wiele zalet, ale jeśli potrzebujesz stworzyć unikatowy klucz zawierający nazwę produktu, powinieneś zobaczyć, czy można go znormalizować.

Indeksowanie kolumn varchar jest czymś, o ile to możliwe, z dala od wszelkich kosztów. Każdy wpis indeksu jest tak duży, jak maksymalny rozmiar kolumny, nawet jeśli zwykle jest to tylko ułamek tego. A jeśli używasz zestawu znaków, takiego jak utf-8, wtedy rozmiar wynosi ~ maxlen + 3.

Przy swoim limicie wydaje się, że zamówienie jest potrzebne. Ale jako FYI, gdy robisz grupę przez, jeśli masz zamiar skonsumować cały zestaw wyników, to wyślij na ORDER BY NULL. Uruchom oba warianty, wyjaśniając, dlaczego; kolejność według wartości null eliminuje domniemany plik filesort i można sortować stronę klienta. (Nie jest to możliwe, jeśli robisz grupę po pakiecie).

0

Powinieneś trzymać się drugiego zapytania. Użyj indeksu na kolumnie, która najbardziej redukuje dotknięte rzędy. W tym przypadku może to być data. jeśli warunki filtrowania zawsze zawierają więcej niż jedną kolumnę, powinieneś wypróbować indeks wielokolumnowy. MySQL użyje tylko jednego indeksu.

0

Jak stwierdził Mitch, próba znalezienia kryteriów, które naturalnie miałyby mniejszą liczbę rekordów, zdecydowanie wygrałaby za wydajność. A jeśli kategoria + płeć byłaby bardzo powszechna, należy utworzyć indeks w dwóch kolumnach. Dodatkowo, po znalezieniu optymalnych kryteriów, możesz zmienić następujące zapytanie, aby lepiej pasowało do niego. "STRAIGHT_JOIN" mówi MySQL, aby zrobił to w podanej kolejności, zamiast próbować zmienić podstawową tabelę używaną do generowania zapytań i łączenia się z drugą ... Więc nie wiem, która jest dokładniejsza z indeksu kategorii , płeć lub data ...Jeśli Date będzie miała mniejszą podstawę zapisu, zamieniłbym to jako pierwszą tabelę w klauzuli FROM, i mentalnie przesunę kryteria IT na datę do pierwszej pozycji klauzuli WHERE (tylko ja osobiście, aby zachować synchronizację z tabelami wizualnie). Widziałem, jak STRAIGHT_JOIN znacznie poprawił wydajność w WIELU sytuacjach, które w innym przypadku okazały się prostymi zapytaniami.

SELECT STRAIGHT_JOIN 
     product_info.*, 
     MIN(product_all.sale_price) as sale_price, 
     product_all.buy_link 
    FROM 
     product_info, 
     product_all 
    where 
      product_info.category = 2 
     AND product_info.gender = 'W' 
     and product_info.prod_id = product_all.prod_id 
     AND product_all.date = '2010-09-30' 
    GROUP BY 
     product_info.prod_id 
    ORDER BY 
     MIN(product_all.sale_price) ASC 
    LIMIT 13