2012-06-22 11 views
5

Mam 3 tabele: produkty, kategorie i pro_cat_link. Produkt można połączyć z jedną lub wieloma kategoriami za pomocą tabeli pro_cat_link.Zapytanie SQL, aby znaleźć produkty pasujące do zestawu kategorii

Moje zapytanie musi odpowiedzieć na następujący problem: znaleźć wszystkie produkty pasujące do zestawu kategorii. Np .: znajdź wszystkie produkty "żółte ORAZ ORAZ słodkie".

Kiedy bada ten problem, więc mogłem tylko znaleźć rozwiązanie, co obecnie używam: Complicated SQL Query--finding items matching multiple different foreign keys

W moim przypadku, moja kwerenda wygląda następująco:

SELECT products.id, COUNT(DISTINCT categories.id) as countCat 
FROM products 
INNER JOIN pro_cat_link ON (pro_cat_link.product_id = products.id) 
WHERE pro_cat_link.category_id IN (3,6,8,10) 
GROUP BY product.id 
ORDER BY product.date DESC 
HAVING countCat = 4 

Innymi słowy, wybierz wszystkie produkty, które pasują do identyfikatorów kategorii (3,6,8,10) i zachowaj tylko te, które pasują dokładnie do 4 kategorii.

Działa to dobrze, ale mam problemy z wydajnością, ponieważ funkcja COUNT(), GROUP BY, ORDER BY powoduje, że indeksowanie jest bardzo ograniczone. Czy ktokolwiek może wymyślić lepszy sposób rozwiązania tego problemu?

+0

Zakładam, że samo robienie połączenia dla każdej kategorii trwa zbyt długo? – Jodaka

+0

@Jodaka Tak. Menedżerowie wymagają, aby użytkownik mógł wybrać tyle kategorii, ile chce;) – Tchoupi

Odpowiedz

2

Można wyeliminować problemy z wydajnością grupowania i liczenia, jeśli gdzieś je przechowujesz. Możesz dodać kolumnę do produktów o nazwie total_categories, która wskaże liczbę kategorii, w których uczestniczy produkt. Następnie możesz po prostu powiedzieć: where total_categories = 4. Może to być trudniejsze do utrzymania, jeśli produkty często zmieniają swoje kategorie, ponieważ musisz stale aktualizować to pole poprawnie - i musisz zdecydować, czy chcesz to zrobić w kodzie aplikacji, w wyzwalaczu lub w procedurze przechowywanej ...

Zwykle nie sądzę, że dobrym pomysłem będzie przechowywanie takich metadanych bezpośrednio w tabeli, ale jeśli wydajność jest naprawdę, to źle, może warto rozważyć.

+0

+1 Pozbycie się 'GROUP BY' to zdecydowanie droga. Dam temu szansę. – Tchoupi

1

Jeśli nie masz zbyt wielu kategorii, zamiast śledzić liczbę kolumn, możesz mieć łańcuch bitów, który reprezentuje kategorie, w których się znajduje (tj. 1 na pozycji oznacza, że ​​produkt należy do kategorii i , a 0 oznacza nie w kategorii). Następnie, wyszukując grupę kategorii, generujesz łańcuch bitowy dla tego wyszukiwania i wszystkie ciągi kategorii z tym ciągiem. Te w odpowiedniej kategorii wytworzą ciąg wyszukiwania jako odpowiedź.

Załóżmy na przykład, że masz dziesięć kategorii. Pozycja 1 znajduje się w kategoriach 1, 3, 5, 6, 8, 10, więc jej ciąg kategorii to 1010110101. Element 2 znajduje się w kategoriach 1, 2, 4, 6, 8, 10, a więc jego ciąg kategorii to 1010101011. Szukając 3, 6, 8 i 10, wygenerowałbyś ciąg s = 1010100100. Item1 & s = 1010100100 = s. Item2 & s = 1010100000 <> s.

Co więcej, nie musisz zapisywać go jako napisu, możesz go po prostu zapisać jako rzeczywisty odpowiednik w bazie 10. Zatem Item1, Item2 i s to odpowiednio 693, 683 i 676. 693 & 676 = 676, ale 683 & 676 = 672. Następnie, jeśli dodajesz produkt do kategorii i, po prostu zaktualizuj jego numer kategorii o 2^(i - 1), a jeśli usuniesz z kategorii i, po prostu odejmij 2^(i - 1).

Oczywiście, jeśli masz więcej kategorii niż bitów w intencie MySQL, to nie będzie działać. Ponadto, jak zasugerował FrustratedWithFormsDes w swojej odpowiedzi, wywołuje to wszystkie problemy związane z aktualizacją zarówno pro_cat_link, jak i tej tabeli (oczywiście w zależności od tego, do czego służy pro_cat_link, może to całkowicie wyeliminować). Ponadto, jeśli kategoria zmienia liczby, musisz zaktualizować wszystko.

+0

+1 za interesujący pomysł! – FrustratedWithFormsDesigner

Powiązane problemy