Jak nastavit v tabulce sloupec pro multiselect a jak zpracovat data z multiselect?
- Zuben45
- Člen | 268
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)
- Zuben45
- Člen | 268
Š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
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
Š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
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
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
Š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
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
Š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í u 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
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
Š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 sloupcezanr1
,zanr2
,zanr3
azanr4
? 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
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
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
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
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
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
Š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)