02. juni 2008 - 19:27Der er
9 kommentarer og 1 løsning
Flere betingelser i en mange-til-mange
Det her burde være ret simpelt, men jeg kan bare ikke få det til at fungere. Jeg har 3 tabeller:
- Posts - Categories - Categorizations
Sidstnævnte er en mange-til-mange tabel mellem Posts og Categories, så den indeholder altså PostId og CategoryId for hver række. Det fungerer helt fint.
Jeg vil nu gerne have de Posts ud som er medlem af to bestemte kategorier. Det jeg har giver altid 0 resultater, selvom der burde være nogle:
SELECT COUNT(distinct Posts.Id) as count FROM Posts INNER JOIN Categorizations ON Posts.Id = Categorizations.PostId INNER JOIN Categories ON Categorizations.CategoryId = Categories.Id WHERE ((Categorizations.CategoryId = 735) AND (Categorizations.CategoryId = 1187))
Jeg forstår faktisk godt HVORFOR det giver 0 resultater, men jeg kan ikke lige regne ud hvordan jeg gør det rigtigt.
Som det lyder til du selv kan indse, så har du jo fx 3 rækker, hvis en post er medlem af 3 kategorier. Du vil tjekke for, om den præcis er medlem af de to som har ID 735 og 1187. Dvs, at du vil have de postings, hvor der er 2 rækker i resultatet. Prøv fx med dette (jeg har ikke lige testet det, men det vil kun virke på en sql 2005):
WITH aCTE AS ( SELECT Posts.Id FROM Posts INNER JOIN Categorizations ON Posts.Id = Categorizations.PostId INNER JOIN Categories ON Categorizations.CategoryId = Categories.Id WHERE (Categorizations.CategoryId = 735) OR (Categorizations.CategoryId = 1187) GROUP BY Posts.Id HAVING COUNT(*) = 2 )
SELECT COUNT(distinct Posts.id) as count FROM aCTE
---------------- Idéen er, at du bruger en cte, som er en slags midlertidig view. Select'en returnerer alle de posts, som enten har categoryid 735 ELLER 1187. Det betyder dog, at den også returnerer de posts, som kun hører til én af kategorierne. Men ved at lave en GROUP BY på posts.id, og så tilføje "HAVING COUNT(*) = 2", så returneres KUN de posts, som netop både en kategori 735 OG en 1187. Ved at putte select'en ind i en CTE, kan du derefter lave en simpel "select count" på cte'en, så du får returneret antallet af posts, som opfylder dit ønske.
Tak for et hurtigt svar. Det kald ser frygteligt dyrt ud. Den skal jo hente alle de posts frem, bare for at finde antallet. Men der er måske ingen billigere måde ... :(
Jeg tror nu ikke det er så slemt. En count er i og for sig heller ikke billig. Bemærk, at alle rækkerne jo ikke returneres til klienten - det er kun internt den junglerer rundt med dataene. Og det vil den også gøre for at lave en alm. count.
Interessant nok. Jeg tror stadig det er ret meget dyrere at oprette sådan en midlertidig samling data, men du har nok også ret i det ikke er overvældende når den ikke skal returnere dem. I stedet for at gisne, prøver jeg :)
Denne linje skulle jeg i øvrigt lige ændre:
SELECT COUNT(distinct Posts.id) as count til SELECT COUNT(distinct Id) as count
Nok fordi den selecter på den midlertidige datakilde og ikke på selve tabellen. Det ser ud til den returnerer et tal der godt kunne være rigtigt :) tak!
SELECT COUNT(DISTINCT p1.id) AS count FROM posts p1 INNER JOIN categorizations pc1 ON p1.id = pc1.postid INNER JOIN categories c1 ON pc1.categoryid = c1.id INNER JOIN posts p2 ON p1.id = p2.id INNER JOIN categorizations pc2 ON p2.id = pc2.postid INNER JOIN categories c2 ON pc2.categoryid = c2.id WHERE (pc1.categoryid = 735) AND (pc2.categoryid = 1187)
Men så vidt jeg kan se så har categorizations alle de felter du skal bruge, så du kan faktisk nøjes med:
SELECT COUNT(DISTINCT pc1.postid) AS count FROM categorizations pc1 INNER JOIN categorizations pc2 ON pc1.postid = pc2.postid WHERE (pc1.categoryid = 735) AND (pc2.categoryid = 1187)
Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.