Autor Zpráva
Tequily
Profil
Zdravím,

mám takový dotaz, týkající se rychlosti.

Vezměme v potaz, že UPDATE jednoho řádku trvá 0,0007s. Když je takovýchto dotazů prováděno 10000, matematicky mi vychází, že by dohromady měly trvat 7s.

Nyní jsem ale importoval soubor, který obsahuje cca 48000 velmi jedoduchých dotazů, které vypadají zhruba takto: "UPDATE tabulka SET cosi = 'kde' WHERE (id = nějakéID AND posloupnost = 1)".

Po cca 15 minutách se mi updatovalo pouze 12000 rádků.

Jak je to tedy s rychlostí prováděných dotazů? Tohle mi totiž vážně nejde do hlavy.

Děkuji za odpovědi, názory, komentáře.
Kajman
Profil
Zkuste si měřit rychlost updatů postupně pro všech 48000 řádků, zda se bude rychlost měnit.

Může to být např. tím, že malý počet se vejde do cache disku. U velkého počtu změn se již cache musí začít přepisovat na disk.
Tori
Profil
Tequily:
Projevuje se to stejně i v situaci, kdy tabulka nemá žádné indexy?
resp. je to spíš otázka na Kajmana: Může mít na rychlost vliv velikost indexu? Že např. velký index by se pomaleji updatoval než index pro pár řádků? (jestli by to tedy mohlo způsobovat rozdíl i u takhle relativně malého množství řádků)
Tequily
Profil
Tori:
To nevím, nezjišťoval jsem. Všechny tabulky jsou totiž indexovány pro maximální rychlost.

Kajman:
Ano, tím cachem by to mohlo být, to je dobrý nápad. Nicméně tu rychlost nezměřím, updatuji přes soubor a odstavil jsem si tím celé PMA dočasně :-)
peta
Profil
"tabulky jsou totiž indexovány pro maximální rychlost"
Nezname strukturu tabulky, co je jak indexovano, nemame vzorek dat (5 radku) a nevime, jake sql dotazy vuci te tabulce posilas. Takze nelze rici, jestli jsou optimalizovany maximalne nebo si to jen myslis.
(id = nějakéID AND posloupnost = 1) - pokud to pouzivat 48.000x, tak uz by se vyplatilo mozna pouzit index spolecny pro 2 sloupce.

Mno, k veci. Napadlo mne, pouzivas to v transakci? Pokud commitujes radek po radku, tak to muze trvat 15 min a ne 7s.
Tequily
Profil
peta:
no, možná pro maximální rychlost to není, to byla špatná formulace

Struktura tabulky: id, pocet, cena, idDostupnosti, druheID, datumUpdate

Indexy: ID -> mohutnost cca 80000, idDostupnosti -> mohutnost 2

SQL: "UPDATE tabulka SET pocet = $pocet, datumUpdate = NOW() WHERE (id = $nejakeID AND idDostupnosti= 1)"

$pocet a $nejakeID získávám z XML, skládám si SQL a ukládám do nového souboru
peta
Profil
Zkousel jsi importovat obsah toho sql souboru pres phpmyadmina? Pripadne zkopirovat tam do zalozky sql ty sql dotazy a kliknout ok?
To vypada totiz, ze 15 minut trva jen to, nez si soubor pripravis a on pak za 0,7s provede jeden dotaz. Takze mas nejspis chybne svuj php kod pro import, ktery to nejspis radek po radku zpracovava a posila do sql a pod.
Zkus toho phpmyadmina a import zalozku (nejdriv si tam rozklikas v menu svoji databazi).
Take muzes pouzit import z prikazove radky, pokud mas pripraveny soubor s sql dotazy.

Schvalne muzes pridat tvuj php import kod, pro zajimavost s kratkou ukazkou (5 radku) ze vstupniho souboru.
Tequily
Profil
peta:
Přes import jsem to zkoušel ;-) taky to nezvládl.

Nicméně problém jsem vyřešil.

SQL jak předtím, tak nyní, vytvářím foreachem, který projíždí XML.

Předtím:
foreach($xml->SHOPITEM as $zam) { 

$sql .= "UPDATE tabulka SET pocet = $pocetKusu, datumUpdate = NOW() WHERE (id = $id AND idDostupnosti= 1);"
}

Nyní 2 možnosti.
Znatelnou změnou také bylo, že jsem NOW() nahradil za $datum = date("Y-m-d")
NOW() je totiž další dotaz pro databázi. Sice trvá neznatelně dlouho, ale při vyšším počtu dotazů už se to projeví. Když dosadíme pevnou hodnotu, skript se nám zase o něco zrychlí.

1. možnost pomalejší:
$sql = "UPDATE dostupnostKniha SET datumUpdate = '$datum', pocetKusu = CASE id";
foreach($xml->SHOPITEM as $zam) { 
$sql .= " WHEN '$id' THEN $pocetKusu";
}
$sql .= " END WHERE (idDostupnosti = 2)";

Tento sql projíždí všechny záznamy s dostupností 2, což je v pořádku, nicméně může nastat chvíle, kdy dané ID v XML není, tedy i v tomto připadě se zkontroluje. To eliminuji v druhém sql, které používám již všude.

2. možnost, velmi rychlá:
$sql = "UPDATE dostupnostKniha SET datumUpdate = '$datum', pocetKusu = CASE id";

foreach($xml->SHOPITEM as $zam) { 
    $sql .= " WHEN '$id' THEN $pocetKusu";
    $idcka .= "'$id',";
}
$idcka = substr($idcka,0,-1); 
$sql .= " END WHERE (id IN($idcka) AND idDostupnosti = 2)";

SQL projede pouze záznamy s dostupností 2 a s ID, které si vybírám. Přestože $idcka obsahuje cca 30 000+ idcek (jak které XML, někde je to až 100 000), je to rychleší.

Jen pro představu
mysqli_query($db, $sql)
mi v prvním, neefektivním případě trvalo několik řádů minut, snad i hodin a hned jsem to vzdal.
V druhém případě trvání skriptu bylo cca 5 minut.
V posledním, velmi efektivním případě, trvá skript pouhé 3 minuty a to včetně překopírování celého XML (60Mb) na náš server!!!.

Pokud tedy někdo budete řešit podobný problém, doporučuji užít metodu WHEN.

Děkuji za účast v diskusi a za případné komentáře, návrhy, vylepšení, či dotazy.
Kajman
Profil
Myslím, že budou i rychlejší varinaty, než s case. Zkuste pro zajímavost změřit takto vygenerovaný dotaz

update `dostupnostKniha` join
       (select 1 id, 10 k
        union
        select 2 id, 20 k
        union
        select 3 id, 30 k
        union
        select 4 id, 40 k) dbtmp on `dostupnostKniha`.id = dbtmp.id and `dostupnostKniha`.idDostupnosti = 2
set     `dostupnostKniha`.pocetKusu = dbtmp.k, `dostupnostKniha`.datumUpdate = '$datum'

Pokud by bylo příliš řádků, dala by se místo toho poddotazu použít temporary tabulku.

Místo case by šly použít i funkce elt a field, ale myslím, že to výrazně rychlejší nebude, jen to trošku zkrátí výsledný dotaz.
UPDATE dostupnostKniha
SET    datumUpdate = '$datum', pocetKusu = Elt(Field(id, 1, 2), 10, 20)
WHERE  id IN ( 1, 2 ) and idDostupnosti = 2
peta
Profil
No vida, uz to z nej leze :) V prvnim prispevku nic o NOW ani CASE nebylo.
id = $id AND idDostupnosti= 1 // tu podminku bych dal opacne a key udelal jako (idDostupnosti,id). Dostupnosti odbouras treba pulku idcek a zjednodusis porovnavani.
Tequily
Profil
peta:
V prvním příspěvku to nebylo, protože CASE tam nebyl žádný :-)

Teď jsem ale narazil na problém další. Potřebuji updatovat 3 sloupce naráz a CASE už to nezvládá, nebo nevím jak jej napsat pro více sloupců, vite jak si s tím poradit?

$sql = "UPDATE dostupnostKniha SET datumUpdate = '$datum', cena= CASE id $sql1 END, sleva = CASE id $sql2 END, dph = CASE id $sql3";
$sql .= " END WHERE (idDostupnosti = 4 AND id IN($idcka))";

$sql1, $sql2 a $sql3 jsou jsou vygenerované WHEN a jsou v pořádku.
Kajman
Profil
Tequily:
a CASE už to nezvládá

Jak konkrétně to nezvládá? Překročíte maximální velikost dotazu? Pak to můžete rozdělit na více updatů.

A [#9] jste četl? Verze s poddotazem nebo s temporary tabulkou by mohla být při více sloupcích kratší i rychlejší.
Tequily
Profil
Kajman:
Ano, četl jsem. Řádků je vskutku hodně, takže spíše bych pracoval s temporary tabulkou. S tou jsem ale ještě nepracoval, takže si o tom musím nejdříve něco přečíst a zatím jsem se k tomu nedostal.

Rozdělení na více updatů však pomohlo, nevím proč jsem to nezkusil dříve.

Vaše odpověď

Mohlo by se hodit

Odkud se sem odkazuje


Prosím používejte diakritiku a interpunkci.

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