Autor Zpráva
Toníček
Profil *
Dobrý den,

Měl bych dotaz s optimalizací rychlosti načítání dat, nejsem databázově.

mám cca 7 tabulek s produkty, a potřebuji vyhledat např. produkty pokud jsou skladem, jestli jsou v této kategorii atd. nebo jestli jsou neoemzené (níže posílám aktuální příkaz).

Mám nastavené indexy (id je primary key / auto_increment) index je product_id, na všech tabulkách. Jakmile se snažím ale připojit kategorii, první dotaz (při 5 000 produktech) trvá 7 sec, poté další (ten stejný) asi cache je 0,00004s, ...za minutu pouštím znova a opět čekat (nebo pokud změním příkaz).

Dokáže mi někdo poradit, jak lépe optimalizovat?

$kategorie = 102;

SELECT pr.* FROM produkty pr
LEFT JOIN produkty_jazyk prl ON pr.id = prl.product_id 
LEFT JOIN produkty_kategorie cat ON pr.id = cat.product_id 
LEFT JOIN produkty_sklad stock ON pr.id = stock.product_id OR pr.id = stock.product_parent_id 
WHERE pr.parent_id=0 AND pr.deleted=0 AND cat.category_id=$kategorie AND (stock.amount>0 OR stock.unlimited=1)
GROUP BY pr.id
ORDER BY pr.price ASC
LIMIT 60, 15

Pro upřesnění skladu - řeším z důvodů variant (př. je skladem jen nějaká varianta u toho produktu, tak je potřebuji projít.):
LEFT JOIN produkty_sklad stock ON pr.id = stock.product_id OR pr.id = stock.product_parent_id

Toto zase určuje sklad (větší než 0 nebo neomezený)
AND (stock.amount>0 OR stock.unlimited=1)

Díky za rady
Keeehi
Profil
Jaký je explain dotazu?
Kajman
Profil
Pro ladění je vhodné používat
select SQL_NO_CACHE pr.*...
čímž zakážete použití té cache.

Obecně indexy by měly být na sloupcích, podle kterých se filtruje, spojují tabulky, někdy i řadí.

Někdy pomůže příkaz
explain select ...
kde se dá poznat, jaké indexy dotaz použil a co dlouho trvá.
Toníček
Profil *
Díky, teď jsem zjistil, že pokud odeberu

"LEFT JOIN produkty_sklad stock ON pr.id = stock.product_id OR pr.id = stock.product_parent_id "
Trvá příkaz cca 0.4s, pokud ho nechám, trvá cca 7s.


Jenže já potřebuji najít jen produkty, které jsou skladem (a to může být např. jen 1 varianta z 50)
Keeehi
Profil
Toníček:
Na product_parent_id je také index?
Toníček
Profil *
Ano


Takže problém je nyní v tomto "OR pr.id = stock.product_parent_id" ale index tam je, sakra :D





Teď mě to napadlo rozdvojit.

LEFT JOIN produkty_sklad stock ON pr.id = stock.product_id
LEFT JOIN produkty_sklad stockV ON pr.id = stockV.product_parent_id

a WHERE ...AND ((stock.amount>0 OR stock.unlimited=1) OR (stockV.amount>0 OR stockV.unlimited=1) )

myslíte že je to takto ok?
Kajman
Profil
Když použiteje příkaz
show create table `tabulka`
Tak to vrátí sktrukturu včetně indexů, můžete sem pro kontrolu indexů dát.

Explain asi také raději textově, pidi obrázky se dobře nečtou.
Toníček
Profil *
Teď už to jde super, jak jsem psal - napojil jsem tam 2 tabulky na místo "OR" v joinu skladu..
Ještě si s tím budu hrát.
Toníček
Profil *
Zdravím,

Nyní mi ale nastal jiný problém. Potřebuji seřadit produkty podle toho, kolik jich je skladem, a to vč. variant :D
Mám to takto, ale opět slow :( Nyní to asi nebere index, protože to vyhledávání PRODUCT_ID OR PRODUCT_PARENT_ID

PS - předešlý problém jsem jak jsem psal ,vyřešil

    SELECT pr.*, (
        SELECT SUM(prstock.amount) FROM  produkty_sklad prstock 
        LEFT JOIN  produkty_sklad npr ON prstock.product_id=npr.id WHERE npr.deleted=0 AND (prstock.product_id=pr.id OR prstock.product_parent_id=pr.id)  
    ) as total_stock 
    FROM produkty pr
    LEFT JOIN produkty_jazyk prl ON pr.id = prl.product_id 
    LEFT JOIN produkty_kategorie cat ON pr.id = cat.product_id  
    WHERE pr.parent_id='0' AND pr.deleted='0' AND pr.del=0 
    GROUP BY pr.id 
    ORDER BY total_stock ASC 
    LIMIT 0, 20



pardón, takto

SELECT pr.*, (
SELECT SUM(prstock.amount) FROM produkty_sklad prstock
LEFT JOIN produkty npr ON prstock.product_id=npr.id WHERE npr.deleted=0 AND (prstock.product_id=pr.id OR prstock.product_parent_id=pr.id)
) as total_stock
FROM produkty pr
LEFT JOIN produkty_jazyk prl ON pr.id = prl.product_id
LEFT JOIN produkty_kategorie cat ON pr.id = cat.product_id
WHERE pr.parent_id='0' AND pr.deleted='0' AND pr.del=0
GROUP BY pr.id
ORDER BY total_stock ASC
LIMIT 0, 20
Kajman
Profil
Můžete si spočítat množství na skladě a to teprve připojit. Pokud to není omezené kategoríí a je potřeba sečíst skoro všechno, bývá to rychlejší než dělat tolik poddotazů na součet, kolik je produktů.

SELECT    pr.*,
          Coalesce(prstock.amount,0)+Coalesce(t.soucet_variant,0) total_stock
FROM      produkty pr
LEFT JOIN produkty_sklad prstock prstock.product_id=pr.id
LEFT JOIN
          (
                   SELECT   prstock_var.product_parent_id,
                            sum(prstock_var.amount) soucet_variant
                   FROM     produkty_sklad prstock_var
                   JOIN     produkty pr_var
                   ON       prstock_var.product_id=pr_var.id
                   WHERE    pr_var.deleted=0
                   GROUP BY prstock_var.product_parent_id) t
ON        pr.id=t.product_parent_id
WHERE     pr.parent_id='0'
AND       pr.deleted='0'
AND       pr.del=0
ORDER BY  total_stock DESC
LIMIT     0, 20


Ale musí se projít na disku a posčítat všechny řádky, tak to bude pomalé. Pokud je u této sestavy důležitá rychlost, je možné si udělat pomocný sloupec nebo pomocnou tabulku, kde se bude upravovat množství i při změně potomka. Také by rychlosti mohlo pomoci, kdyby se smazané věci přesouvaly do jiných tabulek a v těch pro výpočty byly jen aktuální produkty a podprodukty.

EDIT: v tom poddotaze jsem doplnit inner join na produkty s testem, že podprodukt není smazaný. Left join v [#9] ale smazané neomezoval.

Vaše odpověď

Mohlo by se hodit


Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm:

0