2016-09-02 46 views
5

Mam zapytanie, które trwa 10-20 sekund, ale jestem pewien, że można go zoptymalizować, po prostu nie jestem wystarczająco dobry, aby to zrobić. Chciałbym pomóc i wyjaśnić, aby móc zastosować je do podobnych zapytań. Oto moje zapytanie:optymalizacja zapytania mysql z 2 złączeniami i klauzulami grupowymi

SELECT 
     `store_formats`.`Store Nbr`, 
     `store_formats`.`Store Name`, 
     `store_formats`.`Format Name`, 
     `eds_sales`.`Date`, 
     sum(`eds_sales`.`EPOS Sales`) AS Sales, 
     sum(`eds_sales`.`EPOS Quantity`) AS Quantity 
     FROM 
     `eds_sales` 
     INNER JOIN `item_codes` ON `eds_sales`.`Prime Item Nbr` = `item_codes`.`Customer Item` 
     INNER JOIN `store_formats` ON `eds_sales`.`Store Nbr` = `store_formats`.`Store Nbr` 
     WHERE 
     `eds_sales`.`Store Nbr` IN ($storenbr) AND 
     `eds_sales`.`Date` BETWEEN '$startdate' AND '$enddate' AND 
     `eds_sales`.`Client` = '$customer' AND 
     `eds_sales`.`Retailer` IN ($retailer) AND 
     `store_formats`.`Format Name` IN ($storeformat) AND 
     `item_codes`.`Item Number` IN ($products) 
     GROUP BY 
     `store_formats`.`Store Name`, 
     `store_formats`.`Store Nbr`, 
     `store_formats`.`Format Name`, 
     `eds_sales`.`Date` 

Oto wynik wyjaśnić: enter image description here

Jak można tam zobaczyć, próbowałem i stworzył kilka indeksów z kolumnami zaangażowanych w nie wiele sukcesów. Główne opóźnienie spowodowane jest przez skopiowanie do tabeli tymczasowej.

Są stoły zaangażowane:

store_formats:

CREATE TABLE `store_formats` (
`id` int(12) NOT NULL, 
`Store Nbr` smallint(5) UNSIGNED DEFAULT NULL, 
`Store Name` varchar(27) DEFAULT NULL, 
`City` varchar(19) DEFAULT NULL, 
`Post Code` varchar(9) DEFAULT NULL, 
`Region #` int(2) DEFAULT NULL, 
`Region Name` varchar(10) DEFAULT NULL, 
`Distr #` int(3) DEFAULT NULL, 
`Dist Name` varchar(26) DEFAULT NULL, 
`Square Footage` varchar(7) DEFAULT NULL, 
`Format` int(1) DEFAULT NULL, 
`Format Name` varchar(23) DEFAULT NULL, 
`Store Type` varchar(20) DEFAULT NULL, 
`TV Region` varchar(12) DEFAULT NULL, 
`Pharmacy` varchar(3) DEFAULT NULL, 
`Optician` varchar(3) DEFAULT NULL, 
`Home Shopping` varchar(3) DEFAULT NULL, 
`Retailer` varchar(15) DEFAULT NULL 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 
ALTER TABLE `store_formats` 
ADD PRIMARY KEY (`id`), 
ADD UNIQUE KEY `uniqness` (`Store Nbr`,`Store Name`,`Format`), 
ADD KEY `Store Nbr_2` (`Store Nbr`,`Format Name`,`Store Name`); 

eds_sales:

CREATE TABLE `eds_sales` (
`id` int(12) UNSIGNED NOT NULL, 
`Prime Item Nbr` mediumint(7) NOT NULL, 
`Prime Item Desc` varchar(255) NOT NULL, 
`Prime Size Desc` varchar(255) NOT NULL, 
`Variety` varchar(255) NOT NULL, 
`WHPK Qty` int(5) NOT NULL, 
`SUPPK Qty` int(5) NOT NULL, 
`Depot Nbr` int(5) NOT NULL, 
`Depot Name` varchar(50) NOT NULL, 
`Store Nbr` smallint(5) UNSIGNED NOT NULL, 
`Store Name` varchar(255) NOT NULL, 
`EPOS Quantity` smallint(3) NOT NULL, 
`EPOS Sales` decimal(13,2) NOT NULL, 
`Date` date NOT NULL, 
`Client` varchar(10) NOT NULL, 
`Retailer` varchar(50) NOT NULL 
) ENGINE=InnoDB DEFAULT CHARSET=latin1; 
ALTER TABLE `eds_sales` 
ADD UNIQUE KEY `uniqness` (`Prime Item Nbr`,`Prime Item Desc`,`Prime Size Desc`,`Variety`,`WHPK Qty`,`SUPPK Qty`,`Depot Nbr`,`Depot Name`,`Store Nbr`,`Store Name`,`Date`,`Client`) USING BTREE, 
ADD KEY `Store Nbr` (`Store Nbr`), 
ADD KEY `Prime Item Nbr_2` (`Prime Item Nbr`,`Date`), 
ADD KEY `id` (`id`) USING BTREE, 
ADD KEY `Store Nbr_2` (`Prime Item Nbr`,`Store Nbr`,`Date`,`Client`,`Retailer`) USING BTREE, 
ADD KEY `Client` (`Client`,`Store Nbr`,`Date`), 
ADD KEY `Date` (`Date`,`Client`,`Retailer`); 

item_codes:

CREATE TABLE `item_codes` (
`id` int(12) NOT NULL, 
`Item Number` varchar(30) CHARACTER SET latin1 NOT NULL, 
`Customer Item` mediumint(7) NOT NULL, 
`Description` varchar(255) CHARACTER SET latin1 NOT NULL, 
`Status` varchar(15) CHARACTER SET latin1 NOT NULL, 
`Customer` varchar(30) CHARACTER SET latin1 NOT NULL, 
`Sort Name` varchar(255) CHARACTER SET latin1 NOT NULL, 
`EquidataCustomer` varchar(30) CHARACTER SET latin1 NOT NULL 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 
ALTER TABLE `item_codes` 
ADD PRIMARY KEY (`id`), 
ADD UNIQUE KEY `uniq` (`Item Number`,`Customer Item`,`Customer`,`EquidataCustomer`), 
ADD KEY `Item Number_2` (`Item Number`,`Sort Name`,`EquidataCustomer`), 
ADD KEY `Customer Item` (`Customer Item`,`Item Number`,`Sort Name`,`EquidataCustomer`), 
ADD KEY `Customer Item_2` (`Customer Item`,`Item Number`,`EquidataCustomer`); 

więc moje pytania: Jak widać dołączam do 3 tabel i szukam sprzedaży według daty według formatu sklepu. Próbowałem różnych rodzajów połączeń, lub na przykład zamiast łączenia sprzedaży z item_codes i store_formats, łącząc store_formats z innymi, ale z takimi samymi wynikami. Przejmuję także niektóre tablice zmiennych używających IN, ponieważ są one zasilane przez pola wyboru w aplikacji.

  1. Najlepszy sposób połączyć tych tabel
  2. zaproponować najlepsze indeksy na stole
  3. dlaczego mam tabel tymczasowych? czy to z powodu grupy? Czy jest w pobliżu praca?
  4. Jeśli istnieje zapotrzebowanie na tabele tymczasowe, czy mimo to przyspiesza to tworzenie? (Mam już folder danych w macierzy RAID z 8 dysków, ale nadal powolny
  5. Oczywiście wszelkie je alternatywy są mile widziane

UPDATE. Zaktualizowany moje tabele z niektórych sugestii od komentarzach

UPDATE: zmodyfikowany mój my.cnf jak poniżej zwiększona wydajność (My 8GB RAM, 2 rdzenie,/data/tmp jest na nalotu 8 napędowego, tak samo jak gdzie dane są)

tmpdir   = /dev/shm/:/data/tmp:/tmp 
lc-messages-dir = /usr/share/mysql 
skip-external-locking 
expire_logs_days  = 10 
max_binlog_size = 100M 
innodb_buffer_pool_size = 6G 
innodb_buffer_pool_instances = 6 
query_cache_type=1 
+1

To pytanie będzie prawdopodobnie uzyskać bardziej fachowe uwagi na giełdzie [DBA] (https://dba.stackexchange.com/). –

+0

Czy możesz [edytuj] swoje pytanie, aby wyświetlić wszystkie indeksy w tabelach? –

+0

dodano indeksy do pytania –

Odpowiedz

1

Moved wybiera język zapytań do podrzędnych zminimalizuj elementy do łączenia. Wierzę, że MySQL wo zrobiliśmy to już dla ciebie. Sprawdziłbym plan wykonania dla tych informacji.

SELECT 
    stores.nbr, stores.name, stores.format, 
    epos.date, 
    sum(epos.sales) AS Sales, 
    sum(epos.qty) AS Quantity 
FROM 
    (SELECT `Date` as `date`, `EPOS Sales` as sales,`EPOS Quantity` as qty, `Prime Item Nbr` as item_number, `Store Nbr` as store_number 
FROM 
    `eds_sales` 
WHERE 
    `eds_sales`.`Store Nbr` IN ($storenbr) AND 
    `eds_sales`.`Date` BETWEEN '$startdate' AND '$enddate' AND 
    `eds_sales`.`Client` = '$customer' AND 
    `eds_sales`.`Retailer` IN ($retailer)) as epos 

    INNER JOIN 

    (SELECT `Customer Item` as custItem 
    FROM `item_codes` 
    WHERE 
    `item_codes`.`Item Number` IN ($products)) as items ON epos.item_number = items.custItem 

    INNER JOIN 

    (SELECT `Store Nbr` as nbr, `Store Name` as name, `Format Name` as format 
    FROM 
    `store_formats` 
    WHERE 
    `store_formats`.`Format Name` IN ($storeformat)) as stores ON epos.store_number = stores.nbr 
GROUP BY 
    stores.name, 
    stores.nbr, 
    stores.format, 
    epos.date 
+0

świetna modyfikacja, ale dokładnie taka sama wydajność –

2

(Zbyt dużo, aby umieścić w komentarzu, proszę mi wybaczyć za korzystanie odpowiedź.)

Kiedy masz INDEX(a) i INDEX(a,b), były to zbędne i powinny zostać usunięte. Widzę około 5 takich przypadków.

Każdy store_nbr ma dokładnie jeden store_name? W takim przypadku zbędne jest posiadanie store_name w więcej niż jednej tabeli.Nie znam intencji store_formats, ale myślę, że jest to jeden stół do domu store_name. Zauważ, że nie jest to niezgodne rozmiar dla obu typów danych kolumn store_name i dla store_nbr kolumn!

Wydaje się jakby każdy sklep powinien mieć unikalny numer, a jeśli tak, następnie dodać unikalny klucz uniqness (Store Nbr, Store Name) prawdopodobnie powinien być przekształcony PRIMARY KEY(store_nbr). (Niestety, nie zamierzam umieścić spacji w was nazwy kolumn).

Jest rzadko przydatne do rozpoczęcia indeksu z datą, więc pozbyć KEY Date_2 (Date, Client). W jego miejsce dodaj INDEX(Client, store_nbr, Date); które powinno mieć bezpośredni wpływ na szybkość zapytania. Prawdopodobnie zobaczysz zmianę EXPLAIN SELECT....

int(4) - Może chodziło SMALLINT UNSIGNED?

Posiadanie Date w kluczu UNIQUE (lub PRIMARY) jest zwykle "nieprawidłowe". Co to jest "Klient" dokonał dwóch zakupów tego samego w tym samym dniu?

Po dokonaniu tych zmian, pomówmy trochę więcej.

Dla spójności oglądania, proszę podać SHOW CREATE TABLE.

uniknąć tego konstruktu:

FROM (SELECT ...) 
JOIN (SELECT ...) ON ... 

To jest nieefektywne, ponieważ ani Podzapytanie ma indeks do podejmowania JOIN skuteczny.

+0

Cześć, dziękuję za pierwszy punkt, nie byłem pewien jak to działa. W przypadku kolumn powtarzanych te kolumny pochodzą z plików programu Excel, zaimportowanych bezpośrednio do mysql. Planuję je usunąć w przyszłości, ale na razie jest dla nich przydatny w niektórych szybkich pytaniach. Dla unikalności, czy przekonwertuję ten unikalny klucz na pierwotny, czy nadal będzie unikalny? zmienił indeks z datami. zmodyfikowane typy danych pasujące do wszystkich tabel.na datę w tym unikalnym, musi tak być, ponieważ nie mam do czynienia z klientami, ale dane przychodzą do sklepów, więc wyjątkowość jest określona na date-storenbr i innych –

+0

Będę publikować mój show stworzyć tabelę i wydajność jutro. dziękuję –

+0

"PRIMARY KEY" to klucz "UNIQUE" to "INDEX". –

0

warunki ruszyć na tabele połączone z klauzulą ​​WHERE do przyłączenia na ON klauzula:

SELECT 
`store_formats`.`Store Nbr`, 
`store_formats`.`Store Name`, 
`store_formats`.`Format Name`, 
`eds_sales`.`Date`, 
sum(`eds_sales`.`EPOS Sales`) AS Sales, 
sum(`eds_sales`.`EPOS Quantity`) AS Quantity 
FROM `eds_sales` 
JOIN `item_codes` 
    ON `eds_sales`.`Prime Item Nbr` = `item_codes`.`Customer Item` 
    AND `item_codes`.`Item Number` IN ($products) 
JOIN `store_formats` 
    ON `eds_sales`.`Store Nbr` = `store_formats`.`Store Nbr` 
    AND `store_formats`.`Format Name` IN ($storeformat) 
WHERE `eds_sales`.`Store Nbr` IN ($storenbr) 
AND `eds_sales`.`Date` BETWEEN '$startdate' AND '$enddate' 
AND `eds_sales`.`Client` = '$customer' 
AND `eds_sales`.`Retailer` IN ($retailer) 
GROUP BY 
`store_formats`.`Store Name`, 
`store_formats`.`Store Nbr`, 
`store_formats`.`Format Name`, 
`eds_sales`.`Date` 

Utwórz następujące indeksy:

CREATE INDEX IDX001 ON eds_sales (Client,`Store Nbr`,`Retailer`,`Date`); 
CREATE INDEX IDX002 ON store_formats (`Store Nbr`,`Format Name`); 

Jeśli to działa, daj mi znać, a ja wytłumaczę dlaczego.