Jak nastavit v tabulce sloupec pro multiselect a jak zpracovat data z multiselect?

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
Zuben45
Člen | 268
+
0
-

Dobrý den,
mám v tabulce sloupec nazvaný jako ‚zanr‘, do teďka jsem to měl nastavený jen pro jednu hodnotu, ale jelikož se položka dá zařadit do více žánrů, tak jsem se rozhodl že by bylo vhodnější to předělat na multiselect.

v administraci mám (presenter):

$form->addMultiSelect('zanr', 'Žánr:', $zanry)
    ->setRequired('Vyberte žánr hry')
    ->setDefaultValue(array($default->zanr,));

a SQL:

$upravit = array(
  ...
  'zanr'=>$values->zanr,
  ...
  );
  $this->upravit($upravit,$values->id);

Sloupec nyní nastavený na typ SET.

Potřeboval bych poradit jak udělat, abych dostal po výběru v multiselectu vše do jednoho sloupce, stále se mi to nedaří :/

Pro jistotu ještě ve zkratce. Ve formuláři vyberu dvě hodnoty, a potřebuji aby obě hodnoty byli v sloupci ‚zanr‘ např. ‚polozka1‘,‚polozka3‘

a přitom, tyto položky bych mohl bez problému využít u vytváření formuláře setDefaultValue.

Editoval Zuben45 (6. 4. 2014 18:35)

Šaman
Člen | 2666
+
0
-

Tohle musíš řešit vazební tabulkou (je to vazba m:n). V ukázkách hledej příklad s knihami a tagy (je to stejná vazba). Konkrétně ti neporadím, používám LeanMapper a zrovna tohle on luxusně zjednodušuje.

Zuben45
Člen | 268
+
0
-

Šaman napsal(a):

Tohle musíš řešit vazební tabulkou (je to vazba m:n). V ukázkách hledej příklad s knihami a tagy (je to stejná vazba). Konkrétně ti neporadím, používám LeanMapper a zrovna tohle on luxusně zjednodušuje.

Díky za odpověď, ale teď jsem si lámal hlavu a vyřešil jsem to :), jednoduše přes funkce join a explode :)

Šaman
Člen | 2666
+
0
-

No, pak máš ale problém dohledat všechny knihy daného žánru (musíš přes pomalý fulltext) a nebo dokonce několik žánrů.
Tedy ve sloupci budeš mít „detektivní, horror, bestseller“ a budeš chtít třeba hledat „detektivní bestseller“, tak budeš asi muset složit několik fulltextových podmínek.

Při vazební tabulce je pak vyhledávání jednodušší a ORMy (včetně nepravých, jako NDbT) s nimi umí pracovat.

Zuben45
Člen | 268
+
0
-

Šaman napsal(a):

No, pak máš ale problém dohledat všechny knihy daného žánru (musíš přes pomalý fulltext) a nebo dokonce několik žánrů.
Tedy ve sloupci budeš mít „detektivní, horror, bestseller“ a budeš chtít třeba hledat „detektivní bestseller“, tak budeš asi muset složit několik fulltextových podmínek.

Při vazební tabulce je pak vyhledávání jednodušší a ORMy (včetně nepravých, jako NDbT) s nimi umí pracovat.

no já to mám spíš kvůli menu, v menu to mám rozděleno podle žánrů a pak už jen to vyhledávám přes LIKE :)

PS: Jedná se o PC HRY ;), takže FPS, Akční, Strategie,… :). Jinak mám tam ještě vyhledávání, na to použiji stejnou metodu, explode a zase LIKE :), nebo to je špatně ?

japlavaren
Člen | 404
+
0
-

set alebo maska na databaze nieje dobry navrh… napadaju ma iba 2 dovody:

  • som lenivy
  • som mega lenivy

:) obcas som aj ja lenivy nieco menit, ale potom nadavam sam na seba ked sa to nakopi a treba to na rychlo pofixovat ;)

Editoval japlavaren (6. 4. 2014 22:13)

Šaman
Člen | 2666
+
0
-

No, z hlediska návrhu databáze to je špatně vyřešené, protože tvůj návrh nesplňuje ani první normální formu, tedy s takovým sloupcem mohou nastat problémy.

Za druhé LIKE je daleko pomalejší, než JOIN (LIKE je fulltext, zatímco JOIN je spojení tabulek, které umí databázové systémy extrémně dobře zoptimalozovat, v případě problémů s rychlostí pak lze přidat třeba indexy).

Ale jestli si děláš nějakou domácí aplikaci, tak by ti to mohlo fungovat a stačit tak, jak to děláš. Člověk si může dovolit porušit pravidla, pokud ví, že si tím nezadělá na problémy. Jde tedy o to, jakou aplikaci vyvíjíš, kolik dat asi bude obsahovat a kolik uživatelů očekáváš. Je jasné, že kdybys mluvil o žánrech v CSFD.cz, tak to je špatně, na doma by to mohlo stačit.

Ale problém budeš mít třeba pokud budeš chtít ty žánry pridávat/odebírat. Ty je takhle můžeš jen nastavit všechny najednou (což při zpracování multiselectu stačí), ale pokud by ses rozhodl přidat třeba „žánr“ TOP10 všem hrám, které daný rok vévodily žebříčkům prodejnosti, tak s tím budeš mít problém.

Zuben45
Člen | 268
+
0
-

Šaman napsal(a):

No, z hlediska návrhu databáze to je špatně vyřešené, protože tvůj návrh nesplňuje ani první normální formu, tedy s takovým sloupcem mohou nastat problémy.

Za druhé LIKE je daleko pomalejší, než JOIN (LIKE je fulltext, zatímco JOIN je spojení tabulek, které umí databázové systémy extrémně dobře zoptimalozovat, v případě problémů s rychlostí pak lze přidat třeba indexy).

Ale jestli si děláš nějakou domácí aplikaci, tak by ti to mohlo fungovat a stačit tak, jak to děláš. Člověk si může dovolit porušit pravidla, pokud ví, že si tím nezadělá na problémy. Jde tedy o to, jakou aplikaci vyvíjíš, kolik dat asi bude obsahovat a kolik uživatelů očekáváš. Je jasné, že kdybys mluvil o žánrech v CSFD.cz, tak to je špatně, na doma by to mohlo stačit.

Ale problém budeš mít třeba pokud budeš chtít ty žánry pridávat/odebírat. Ty je takhle můžeš jen nastavit všechny najednou (což při zpracování multiselectu stačí), ale pokud by ses rozhodl přidat třeba „žánr“ TOP10 všem hrám, které daný rok vévodily žebříčkům prodejnosti, tak s tím budeš mít problém.

No na doma to opravdu nemám, právě očekávám trochu větší návštěvnost :).

Takže JOIN místo LIKE bude lepší ? No vyzkouším, ale zase ve vyhledávání je lepší LIKE ne ? :), ale jak píšeš JOIN spojuje tabulky, na co mi bude spojení tabulek u výběru žánru ? :). PS: Mám jen 8 žánrů, a to podle mě projede raz dva ten sloupec i kdyby byli vybrány všechny žánry ne ? :)

S tím TOP10 bych to do žánru nedával a raději napsal článek a nebo novinku :)

Editoval Zuben45 (6. 4. 2014 23:24)

Šaman
Člen | 2666
+
0
-

Jestli chceš mít zaručenou konzistentní databázi, tak si nastuduj Normalizaci relační databáze. Odkaz neuvádím, je toho plno, zkus najít nějaký, který ti připadá dobře vysvětlený. Tvůj návrh nesplňuje hned první a základní bod který říká, že každý sloupec nese jediný a nedělitelný atribut.

S tou nedělitelností je trochu problém, protože třeba adresu mohu považovat za nedělitelnou (pokud ji používám jen pro tisk na obálky), ale pokud někdy budu chtít vyhledávat třeba všechny klienty z jednoho města, rázem se stává adresa dále dělitelnou a tedy mám špatný návrh. U tebe je to ale jasné – v jednom sloupci máš více položek, kterým od začátku vymýšlíš způsob, jak je dělit. A jak vidíš, místo standardních nástrojů pro práci s databází musíš používat vlastní obezličky. Jestli ten projekt má být větší, nebo se plánuje dlouhodobý rozvoj, tak to určitě změň. Ušetříš si problémy, které zatím ani netušíš. Nech to pouze v případě, že víš určitě, že ti možnosti jednoho sloupce bude vždy stačit. A že kromě tebe se do styku s tou databází nedostane jiný programátor.

Zuben45
Člen | 268
+
0
-

Šaman napsal(a):

Jestli chceš mít zaručenou konzistentní databázi, tak si nastuduj Normalizaci relační databáze. Odkaz neuvádím, je toho plno, zkus najít nějaký, který ti připadá dobře vysvětlený. Tvůj návrh nesplňuje hned první a základní bod který říká, že každý sloupec nese jediný a nedělitelný atribut.

S tou nedělitelností je trochu problém, protože třeba adresu mohu považovat za nedělitelnou (pokud ji používám jen pro tisk na obálky), ale pokud někdy budu chtít vyhledávat třeba všechny klienty z jednoho města, rázem se stává adresa dále dělitelnou a tedy mám špatný návrh. U tebe je to ale jasné – v jednom sloupci máš více položek, kterým od začátku vymýšlíš způsob, jak je dělit. A jak vidíš, místo standardních nástrojů pro práci s databází musíš používat vlastní obezličky. Jestli ten projekt má být větší, nebo se plánuje dlouhodobý rozvoj, tak to určitě změň. Ušetříš si problémy, které zatím ani netušíš. Nech to pouze v případě, že víš určitě, že ti možnosti jednoho sloupce bude vždy stačit. A že kromě tebe se do styku s tou databází nedostane jiný programátor.

Díky moc za rady, teď jsem teda vytvořil 3 další sloupce (zanr2,zanr3 a zanr4), které předám pěkně do db, ale pak na znovuoznačení v editaci jsem si musel vytvořit menší funkci, abych to měl v array :), jo jinak je možné omezit počet označeníselect multiple ? Asi JQuery budu muset použít že ? :)

No pokud by to někomu pomohlo tak Jquery (každý ať si to upraví dle své libosti ;) )

$("select").on("click", "option", function (event) {
    if (4 <= $(this).siblings(":selected").length) {
        $(this).removeAttr("selected");
    }
});

Editoval Zuben45 (7. 4. 2014 23:29)

Šaman
Člen | 2666
+
0
-

Fajn, tím jsi sice splnil 1.normální formu optimalizace databáze, ale zadělal sis na problém s vyhledáváním. Jak teď položíš dotaz, WHERE zanr = detektivni AND zanr = horror? To budeš hledat prohledávat všechny sloupce zanr1, zanr2, zanr3 a zanr4? A navíc jsi omezený počtem sloupců.

Věř tomu, že se to běžně dělá přes vazební tabulku (v této ukázce je to tabulka book-tag).

Jdeš na to tou složitější cestou – zkusíš nejdřív všechny slepé cesty. :) Pokud tě to neodradí, tak to má výhodu, že budeš vědět, proč jsou slepé. Druhá možnost je udělat to tak, jak ti radí většina a doufat, že ta většina ví, proč to říká.

Zuben45
Člen | 268
+
0
-

Šaman napsal(a):

Fajn, tím jsi sice splnil 1.normální formu optimalizace databáze, ale zadělal sis na problém s vyhledáváním. Jak teď položíš dotaz, WHERE zanr = detektivni AND zanr = horror? To budeš hledat prohledávat všechny sloupce zanr1, zanr2, zanr3 a zanr4? A navíc jsi omezený počtem sloupců.

Věř tomu, že se to běžně dělá přes vazební tabulku (v této ukázce je to tabulka book-tag).

Jdeš na to tou složitější cestou – zkusíš nejdřív všechny slepé cesty. :) Pokud tě to neodradí, tak to má výhodu, že budeš vědět, proč jsou slepé. Druhá možnost je udělat to tak, jak ti radí většina a doufat, že ta většina ví, proč to říká.

Děkuji, tak jsem tedy vytvořil novou tabulku, se sloupci game_id a zanr a funguje dobře a je to přehlednější :)

Šaman
Člen | 2666
+
0
-

No, jestli to dobře chápu, tak v té nové tabulce máš buď jen jedinou hru pro každý žánr, nebo se ti žánry opakují? Takže jsi zase na začátku, kdy pokud budeš chtít udělat hromadnou operaci (třeba změnit název žánru), tak budeš muset měnit spoustu záznamů. Navíc ti databáze sama napohlídá konzistenci (pokud se ti do té tabulky dostane žánr s překlepem, tak to nikdy nezjistíš, ale hra ti bude v seznamu her toho žánru chybět).
Navíc se ti špatně získá seznam všech žánrů. Pokud nějaký žánr nemá žádnou hru, jakoby nebyl (musel bys je udržovat v aplikaci, ale zhlediska databáze nikde neexistuje seznam žánrů).

Takže tu tabulku, co jsi vyrobil použij jako vazební mezi entitami HRA a ZANR, většinou se dnes tabulky označují v jednotném čísle a vazební tabulky se pak vytváří ve tvaru HRA_ZANR se sloupci hra_id a zanr_id. Databáze si pak může sama kontrolovat konzistenci, tedy že v této tabulce propojuješ jen existující žánry s existujícími hrami. Pokud vymažeš hru, smažeš všechny její záznamy a pokud vymažeš žánr, tak taky (se současným návrhem bys při smnazání/přejmenování žánru musel dělat tyto operace ručně).

Jednoduše získáš seznam všech dostupných žánrů (pro ten multiselect), přidání žánru znamená jen přidat řádek do tabulky ZANR, z principu ti nemůže vzniknout překlep (spojuješ idečka, ne texty), databáze bude menší (neopakuje se ti název žánru na více místech) a pokud budeš potřebovat tabulku ZANR rozšířit třeba o popis, tak jen přidáš nový sloupec do tabulky ‚ZANR‘, teď bys to ani neměl kam napsat.

Toho, že by práce s tím byla složitější, nebo méně efektivní se neboj. Pokud používáš nějaké ORM, tak tu vazbu za tebe často provede samo, pokud si píšeš vlastní SQL, tak jen přidáš jeden řádek JOIN… ON…
Efektivita vzroste, protože relační databáze používají pro spojování tabulek matematiku relací, kterou umí extrémně dobře optimalizovat, zatímco procházení textu je vždy pomalé (takže je lepší spojovat přes id, než přes text, proto tabulka ZANR bude mít minimálně zanr_id a textový sloupec – já dávám název label, nebo name).

Zuben45
Člen | 268
+
0
-

ajo vlastně, díky moc, rychle to změním, jinak tabulku s žánry mám vytvořenou, takže můžu přidávat, mazat a editovat dle potřeby, ale v případě mazání budu muset zajistit nějaké opatření určitě.

Jinak, jak vlastně funguje to spojování (game_id s id v tabulce game a zanr_id s id v tabulce zanry) ? musím nějak naindexovat nebo tak (myslím v db) ? Toto je pro mě trochu nové :), vždy jsem to až řešil v kódu.

Právě žádné ORM nepoužívám, takže to bude problém ?

Editoval Zuben45 (9. 4. 2014 22:18)

Šaman
Člen | 2666
+
0
-

Problém to nebude, jen ti ORM může zjednodušit práci s vazbami obecně. A indexy nepotřebuješ, protože všude používáš primární klíče, které jsou indexované automaticky. Indexy pak můžeš přidat kdykoliv, až/pokud budeš vědět které dotazy zdržují. Indexovat všechno bezdůvodně také není dobré.

Předpokládám, že používáš MySQL a tabulky InnoDB, ty si umí hlídat konzistenci a nedovolí ti třeba smazat žánr, který má nějaké existující vazby. Resp. je možné si nastavit co se má stát při pokudu o smazání – smazat všechno na co jsou ty vazby, tedy vymazat i všechny odkazdy na žánr v té spojovací tabulce (volba CASCADE), nebo zakázat smazat (RESTRICT), myslím, že jde i nastavit místo smazaného žánru NULL, ale to je v tomto případě nesmysl. A když píšu nedovolí, tak na úrovni databáze, takže ani ručně, z Adminera.

Zuben45
Člen | 268
+
0
-

aha, no já asi spíš myslel místo těch indexů cizí klíče :). Takže není nutné nastavovat pro tabulku gamelist_zanr sloupec game_id nastavovat cizí klíč (nebo index, už jsem z toho zmatený :) ) k gamelist pro sloupec id, a gamelist_zanr->zanr_id k gamelist_zanry->id ? (gamelist je seznam her s daty, gamelist_zanry je pro všechny žánry a gamelist_zanr mám na ten „most“ mezi těmi dvěma).

Šaman
Člen | 2666
+
0
-

Adminer nastavuje cizi klíče za tebe. Nejdřív si připrav ty dvě základní tabulky (a doporučuji zvolit jednoslovný název, dvouslovný jen u té spojovací, jinak se ti budou v dotazech plést gamelist_zanry a gamelist_zanr) a pak si vytvoř spojovací tabulku a jako typ sloupce vyber přímo tabulku, na kterou ten sloupec ukazuje. Adminer ti pak vytvoří sloupec se správným typem a velikostí a navíc vytvoří cizí klíč a index (u kterého pak můžeš nastavit CASCADE, RESTRICT, …).
Doplňkově pak můžeš třeba časem přidat indexování i nad sloupcem s názvem hry a tím urychlit vyhledání zánamu podle názvu (ale nikoliv vyhledávání pomocí LIKE). To se dá ale přidat kdykoliv později, až budeš vědět, že je to potřeba.

Zuben45
Člen | 268
+
0
-

aha, díky moc :)

Jsem to pořád dělal přes phpmyadmin :), už by to mělo fungovat, ještě jednou děkuji :)

Jen nechápu, proč u některých tabulek nemohu přidat cizí klíč :), když nejde přidat, tak není ani v položce Typ :)

Editoval Zuben45 (10. 4. 2014 1:21)

Šaman
Člen | 2666
+
0
-

Cizí klíč můžeš dělat jen z indexovaných sloupců, ideálně z primárních klíčů. Tak pokud máš ještě z PhpMyAdmina tabulky, kde není sloupec id indexovaný, tak se ti nenabídne jako typ, nebo cizí klíč.

Zuben45
Člen | 268
+
0
-

Šaman napsal(a):

Cizí klíč můžeš dělat jen z indexovaných sloupců, ideálně z primárních klíčů. Tak pokud máš ještě z PhpMyAdmina tabulky, kde není sloupec id indexovaný, tak se ti nenabídne jako typ, nebo cizí klíč.

no zjistil jsem, že se mi ty tabulky nevytvářeli v Innodb :), jsem myslel, že když mám v db nastavenou, tak to bude automaticky :), ale už to funguje :)

Editoval Zuben45 (12. 4. 2014 0:39)