Autor Zpráva
MagicPowa
Profil
Zdravím, bude to trochu obecný ale snad na to existuje řešení a někdo bude schopnej mě nasměrovat jak dále.

Mám tabulku s pár záznamy, momentálně do 10ti záznamů.
Vypíšu je do řádků pod sebe a udělal jsem si skript, díky kterému je můžu posouvat.
Nahoru a dolů jsou odkazy díky nimž to krokově řadím. 1 klik - posun o 1.

1 | Položka 1 | Nahoru | Dolů
2 | Položka 2 | Nahoru | Dolů
3 | Položka 3 | Nahoru | Dolů atd.

Stává se mi toto:
pokud klikám POMALU, klik => proběhne GET => mám tam SELECT => UPDATE => dobrý
pokud klikám RYCHLE, klik klik klik... => proběhne GET => SELECT a ještě než se to UPDATNE tak proběhne rovnou další SELECT a ten vybere špatnou hodnotu, kvůli tomu, že ještě neproběhnul ten UPDATE

Nejdříve jsem si myslel, že to není možné jak může nějakej dotaz předběhnout následující, když ještě nedoběhnul ale po opakovaném zkoušení se mi potvrdilo to co píšu.
Localhost a DB mám na vlastním počítači, je možné, že na webovém serveru u hostingu by to nedělalo, protože ten je rychlejší a SQL dotazy se vykonávaj rychleji ale na to se nechci spoléhat.

Setkal se s tím někdo? Jak to řešit?

Děkuji za každou pomoc, v případě, že by bylo nutné ukázat zdrojový kód ukážu ho ale momentálně si myslím, že to není nutné je to jen pár řádků základních věcí.
Alphard
Profil
Průběh serverového scriptu by měl být zlomek sekundy, zdržení bych tipoval spíš někde na cestě, což se v ostrém provozu zřejmě ještě zhorší.
Jde spíš o mechanismus posunu, používáte AJAX nebo se obnovuje celá stránka? Neměl byste umožnit další akci, dokud neproběhla ta předchozí.
Pro zrychlení je možné využít javascript, velice rychle posunovat pouze na stránce (třeba i Drag&drop) a na závěr tlačítko Uložit, které odešle nové pořadí na server.
imploder
Profil
MySQL (41) - Transakce - Linux Software
MagicPowa
Profil
Alphard:
AJAX nepoužívám, a ani nehodlám. To moje je jen klasické $_GET v PHP s 1x SELECTEM a 2x UPDATEM.
Pro zrychlení se mi JS, jQuery používat nechce, jednak protože to neovládám a jelikož mi to přijde jako záplata nedokonalosti jiné věci.

imploder:
Článek jsem přečetl i další navazující, který jsem neúspěšně, protože je přímo na dotaz v DB a ne pomocí PHP.
Googlil jsem tedy transakce v mysql_query a využil:

mysql_query("START TRANSACTION;");
mysql_query("SELECT...");
mysql_query("UPDATE...");
mysql_query("UPDATE...");
mysql_query("COMMIT;");


Zápis prošel, dokonce to nehlásilo ani chybu ale co se týče funkčnosti, tak problém přetrvává. Je možné, že to nemám zapsáno správně. Nápady?
Alphard
Profil
Nemyslel jsem to jako záplatu, ale jako pohodlnější možnost.

To moje je jen klasické $_GET v PHP s 1x SELECTEM a 2x UPDATEM.
Tohle mi připadá jako nedostatečný popis. Jestli se odešle chybný požadavek, nezachrání to ani transakce, proto jsem ji ani neodkazoval.
Jak váš systém funguje?
1. stránka s odkazem
2. klik na odkaz
3. odeslání požadavku a současný reload stránky
4. stránka je znovu načtená, jsou na ní vygenerované nové odkazy
5. pokračuje krok 2
Takhle by mělo vše fungovat. Kde je u vás chyba? Ideální by byla živá ukázka.
MagicPowa
Profil
Takže jsem udělal ukázku a dal na web. Je to rychlejší než u mě, jak jsem předpokládal, takže musíte klikat rychleji. K nalezení je tady.

Před tím, než to vyzkoušíte, tak si to zresetujte tlačítkem aby jste měli indexy řazení 1 2 3 4.
Jakmile budete mít např. 1 3 3 4 už je to špatně, zresetujte a vyzkoušejte znova.
TomášK
Profil
Alphard:
Javascript by to z hlediska praktického pravděpodobně vyřešil, ale "čisté" řešení by nemělo spoléhat na to, že mezi požadavky bude nějaká prodleva nebo že uživatel pošle dvakrát tentýž požadavek. Myslím, že by databáze měla být taková, aby k podobné situaci nedošlo, ať se děje co se děje.
Zdá se mi, že transakce zachrání i špatný požadavek - aspoň ve smyslu, že databáze bude v konzistetním stavu (i když možná ne tom, který uživatel chtěl). Není to výhra, ale lepší než současný stav.

MagicPowa
Tipuju, že k chybě možná dojde, pokud se odešle dvakrát tentýž požadavek, tj. uživatel klikne ještě než se nová stránka začne načítat. To bych se pokusil detekovat a případné další odeslání ignorovat. A dále bych ošetřil skript tak, aby databáze zůstala vždy v konzistetním stavu.
Pokud chtete využít transakce, zkontroloval bych, jestli není nastavený autocommit, případně jestli jsou tabulky v InnoDB, jinak myslím nefungují. Případně se mi zdá, že by dotazy šly zredukovat zredukovat na jeden UPDATE:

- předpokládám,že v proměnné chci prohodit záznamy na pozicích $pos a $pos+1:
UPDATE item SET position=position+IF(position=$pos, 1, -1) WHERE position IN ($pos, $pos+1)


Edit: Opraveno dle upozornění Kajmana, dík
Kajman_
Profil *
position+=IF(position=$pos, 1, -1) bude asi nutné rozepsat jako
position=position+IF(position=$pos, 1, -1)
josh
Profil *
Kajman:
position=position+IF(position=$pos, 1, -1)
můžu se zeptat ohledně tohoto zápisu?

Tento UPDATE (společně se zbytkem Tomášova kódu) prohodí pozici dvou řádků? Já to vždy dělal dvěma dotazy, ale pokud jsem to dobře pochopil, tohle by bylo asi lepší. Nebo se pletu?
TomášK
Profil
josh:
Ano, mám za to, že uvedený kód prohodí dva řádky. K řádku se stejnou pozicí jako je v proměnné $pos přičte 1, od ostatních odečte 1. WHERE zajistí, že pracuju jen se záznamem na pozici $pos a následujícím. Výhodou jednoho dotazu je, že proběhne atomicky, tj. nemusím přemýšlet o tom, co se stane, když běží současně dva skripty a dotazy se provádí "na střídačku". Stejnou funkčnost poskytnou dva dotazy a trasnakce, kód bude pravděpodobně čitelnější. Případně pokud jsou ty dva dotazy udělané tak, že jim nevadí, když budou proložené jinými dotazy, je to v pořádku. Je ale potřeba vědět, že s tím může být problém a nějak ho řešit, případně i pštrosím algoritmem.
MagicPowa
Profil
Tak jsem to zkusil pomocí UPDATU s IF a WHERE IN, zkrátka skript od TomášK a funguje to perfektně. Eliminuje to přesně ten problém co jsem potřeboval!

Ještě tam vznikal problém přitom, když jsem vymazal některý záznam, takže řazení bylo 1, 2, 3, -, 6, 7 ale to jsem vyřešil dosazením rozdílu za jedničku +(1).

Jinak ten UPDATE s IF a WHERE IN tam mám dvakrát pro řazení nahoru a dolů, protože u jednoho odečítám a u druhého přičítám.

Pokud by někdo chtěl postnout ten skript, tak ho sem vložím.

Díky všem, kteří mi pomohli tohle vyřešit!

//na předchozím odkaze jsem to neupravoval, je tam pořád staré řešení
MagicPowa
Profil
Ještě jsem zkoušel staré řešení s vypnutým i zapnutým autocommitem, tabulky na InnoDB a dělalo to stejně problém s duplikací indexů řazení.
josh
Profil *
MagicPowa:
Pokud by někdo chtěl postnout ten skript, tak ho sem vložím.
mohl bys, prosím? Nejsem si jistý, jak to udělat druhým směrem, ty plus a mínus jedničky mě tam matou :-)

Děkuji.
MagicPowa
Profil
$presun = GET - id_kategorie
$umisteni = GET - nahoru, dolu
$razeni = GET - razeni

  public static function posouvani_nahoru_dolu($tabulka, $sloupec_id, $sloupec_razeni, $presun, $umisteni, $razeni) {
    if(isset($presun)) {
      if($umisteni=="nahoru") {
        $radit_podle = "DESC";
        $znamenko = "<";
      } elseif($umisteni=="dolu") {
        $radit_podle = "ASC";
        $znamenko = ">";
      }
      
      $vyber_presun = mysql_query("SELECT ".$sloupec_id.", ".$sloupec_razeni." FROM ".$tabulka." WHERE ".$sloupec_razeni." ".$znamenko." '".$razeni."' ORDER by $sloupec_razeni $radit_podle") or die (mysql_error());
      $data_presun = mysql_fetch_array($vyber_presun);
      $radit = $data_presun['razeni']; 
      
      if($data_presun['razeni']!=NULL && $umisteni=="nahoru") {   
        $rozdil = $razeni - $data_presun['razeni'];  
        mysql_query("UPDATE ".tabulka." SET $sloupec_razeni=$sloupec_razeni+IF($sloupec_razeni=$radit, $rozdil, -1) WHERE $sloupec_razeni IN ($radit, $radit+$rozdil)") or die(mysql_error());
      } elseif($data_presun['razeni']!=NULL && $umisteni=="dolu") { 
        $rozdil = ($razeni - $data_presun['razeni'])*-1;  
        mysql_query("UPDATE ".tabulka." SET $sloupec_razeni=$sloupec_razeni+IF($sloupec_razeni=$radit, -$rozdil, 1) WHERE $sloupec_razeni IN ($radit, $radit-$rozdil)") or die(mysql_error());
      }
    }
  }
  
String::posouvani_nahoru_dolu(tabulka, id_sloupec, "razeni", $_GET['presun'], $_GET['umisteni'], $_GET['razeni']);
  

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: