lze nějak použít UNION a INTERSECT?
- Šaman
- Člen | 2666
Ahoj, mám klasickou vazbu M:N ‚book‘ – ‚book_tag‘ – ‚tag‘.
Prví otázka mimo téma zní: lze získat Selection se všema tagama nějaké knížky? Tedy nechci ‚$bookTags‘ (ze které tagy získám po iterování pomocí ‚$bookTag->tag->text‘), ale rovnou ‚tags‘ (po iterování chci ‚$tag->text‘)
A hlavní otázka zní: Jak vybrat knihy, které mají více tagů? Přes klasický AND to nejde, protože ve spojovací tabulce neexistuje řádek, který by odpovídal více tagům. V prostém SQL bych to řešil INTERSECTEM dvou SELECTŮ na dva tagy. Lze to nějak v Nette\Database?
Díky.
Editoval Šaman (3. 3. 2013 10:03)
- enumag
- Člen | 2118
ad M:N vazba
Přímo přes related to řešit nelze, ale můžeš udělat tohle:
$connection->table('tag')->where('book_tag:book.id', $book->id);
ad více tagů
Řešení jsem tu už někde viděl. Bylo to myslím nějak takhle:
$tags = array(...);
$connection->table('book')->where('book_tag:tag.name', $tags)->group('book.id', 'COUNT(book_tag:tag.name) = ' . count($tags));
- Šaman
- Člen | 2666
Díky za rychlou odpověď, ono to funguje, dokonce to vygeneruje jediný
dotaz :)
Ale sám bych takovýhle dotaz nesestavil, cítím z toho čáry :D
//Edit: Tak už jsem pochopil jak to, že to funguje, ale chvíli to trvalo.
Asi to bude i rychlejší, než ten INTERSECT.
A bez klauzule HAVING to vrátí knihy, které obsahují některý
z uvedených tagů, tedy se to tváří jako OR.
Editoval Šaman (3. 3. 2013 10:41)
- enumag
- Člen | 2118
Když jsem to viděl poprvé, byl jsem na tom podobně. ;-) Teď už mi to připadá celkem normální, stačí vědět jak funguje backjoin, group a having. :-P
Jinak trochu se mi tam nelíbí to count($tags)
, bylo by lepší
použít SqlLiteral a ten count dát jako parametr, což ale zatím nejde.
Editoval enumag (3. 3. 2013 10:40)
- Šaman
- Člen | 2666
@castamir:
<script>
SELECT `book`.`id`, `book`.`title`
FROM `book`
INNER JOIN `book_tag` ON `book`.`id` = `book_tag`.`book_id`
INNER JOIN `tag` ON `book_tag`.`tag_id` = `tag`.`id`
WHERE (`tag`.`text` IN ('historie', 'deník'))
GROUP BY `book`.`id`
HAVING COUNT(`tag`.`text`) = 2
</script>
Dotaz až po WHERE včetně vrátí řádek pro každou knihu, která má daný tag, má-li více tagů, bude na více řádkách.
GROUP BY spojí knihy na více řádkách do jednoho řádku (tedy máme všechny knihy, které mají alespoň jeden tag)
A HAVING omezí výsledek jen na knihy, které mají zadaný počet tagů, tedy byly ve výsledku pro každý tag. (máme knihy které mají všechny zadané tagy)
Editoval Šaman (3. 3. 2013 10:51)
- Šaman
- Člen | 2666
Ale už jsem přišel na to, jak to obelhat.. Pokud ve spojovací tabulce existují duplikované záznamy, tak se nám může ve výsledku objevit kniha, která má jen jeden ze zadaných tagů, ale tento řádek je tam dvakrát. Takže je potřeba nastavit UNIQUE a počítat s tím při vkládání nových záznamů.
// @enumag: Viděl, jinak bych se nikdy nenaučil trik se
špičatým kloboukem s dvojtečkou.
Editoval Šaman (3. 3. 2013 11:03)
- castamir
- Člen | 629
@enumag neviděl. V Planette ji nevidím…
ale kamarád google něco našel…
Editoval castamir (3. 3. 2013 15:51)
- enumag
- Člen | 2118
@Šaman: Ano, ten UNIQUE constraint jsem mlčky předpokládal. :-) Považuji jej u všech M:N relačních tabulek za samozřejmost.
@castamir: Tak to se omlouvám že jsem neposlal link rovnou, myslel jsem, že ji znáš. Našel jsi to správně, ty triky se špičatým kloboukem bez ní pochopit opravdu snad ani nelze. ;-)