Haczyk67
5.12.2009, 15:54:19
Mam tabelę z polami "tag" oraz "produkt". Może się zdarzyć że produkty maja takie same tagi, a więc zawartość pola tag często sie powtarza.
Chciałbym pobrać 5 najpopularniejszych tagów, ale nie chcę pisać 5 osobnych zapytan tylko optymalnie zmiescic sie w jednym.
Na razie mam coś takiego:
SELECT DISTINCT tag FROM tagi LIMIT 5
Jak najlepiej dodać do tego pobieranie ilości wystąpień tagu? No i oczywiście jak ułożyć WHERE żeby pobrało 5 najpopularniejszych?
Crozin
5.12.2009, 17:39:04
IMO masz zły projekt bazy danych. Produkty oraz Tagi to raczej relacja n-n, więc powinieneś mieć trzy tabele:
Produkt (id, nazwa)
Tag (id, nazwa)
ProduktTag (id_produkt, id_tag)
A wybranie najpopularniejszych tagów to: COUNT() + GROUP BY + ORDER BY + LIMIT
Haczyk67
5.12.2009, 19:06:39
Bazy już niestety zmienić nie moge.
Nie wiem czy dobrze Cię zrozumiełem. Sugerujesz że mam:
1) wysłać select COUNT(tag) from (...)
2)w php znaleść najpop. rekordy
3) wysłać zapytanie po 5 top rekordów używając group by ?
Crozin
6.12.2009, 10:00:25
Nie, powinieneś to zrobić bezproblemowo jednym zapytaniem:
1) Wybierasz: tag, COUNT(tag) - ewentualnie inne kolumny. Pamiętaj by dla COUNTa dodać sobie jakiś alias typu: cnt
2) Grupujesz wyniki wg kolumny tag
3) Sortujesz po wyniku COUNTa - czyli po wspomnianym aliasie cnt
4) Ograniczasz wyniki do 5-ciu
Haczyk67
8.12.2009, 21:26:37
Chyba jednak przebuduje baze.
Ale to co podałeś id_produkt, id_tag <----- aktualnie mam coś takiego tylko zamiast id tagu mam nazwę tagu
Czyli to samo co napisałem na początku.
Crozin
8.12.2009, 22:36:22
Mając nazwę zamiast ID strasznie sobie komplikujesz życie, bo powtarzasz masę danych w bazie.
SELECT tag, COUNT(tag) AS tag_count FROM tbl_name GROUP BY tag ORDER BY tag_count DESC LIMIT 5;
Haczyk67
9.12.2009, 15:20:33
Ale jeden tag może pasować do wielu różnych produktów a więc jeśli dam ID nic to niezmieni, dalej będę miał masę powtórzeń.
Crozin
9.12.2009, 17:18:11
Tak, ale operowanie na liczbach jest nieporównywalnie lżejsze od męczenia się ze stringami.
Haczyk67
9.12.2009, 17:45:17
OK, w takim razie powiedz mi ktore rozwiazanie jest lepsze:
1)
Produkt (id, nazwa)
Tag (id, nazwa)
ProduktTag (id_produkt, id_tag) <-- i tu masa duplikatów
czy może
2)
Produkt (id, nazwa, tagi(czyli np. "tag1, tag2"))
Tag (id, nazwa, produkty(czyli np. "produkt1, produkt2")
Aha i mówisz że liczby są lepsze, a więc tag1, tag2 i produkt1, produkt2 to ID
Crozin
9.12.2009, 17:54:26
Jakiekolwiek wyszukiwanie, porównywanie czy łączenie w drugim przypadku będzie katorgą. Poza tym nie chodzi o powtarzanie się jakichkolwiek danych, a o rzeczy typu: nazwa tagu, nazwa produktu itp., ID to co innego.
Przy podziale:
Produkt (id, nazwa)
Tag (id, nazwa)
ProduktTag (id_produkt, id_tag)
Nie ma żadnych duplikatów. Dlaczego? Wyjaśnię bo sam tego używam ( w nieco innej wersji ) i napisze JAK używam. Jeśli to UPDATE to wpierw usuwasz wszystkie wpisy w ProduktTag z danym id produktu. Potem robisz z tagów tablice (explode gdzie przecinek to delimiter), trimujesz elementy i robisz array_unique

Teraz tylko pozostaje stworzenie wpisów w ProdutTag. Jeśli to INSERT nowego produktu to nie usuwasz wcześniej z ProduktTag nic. Kluczem jest tutaj zastosowanie przed zapisem do bazy sprawdzenia czy dany tag już istnieje w bazie. To kwestia zapytania do bazy i później użycia tego co dostałeś i array_diff oraz zapisu nowych tagów do bazy tagow. Sam podobnie robię bez jakichkolwiek dubli w bazie. Kwestia to przemyślany dobrze algorytm.
Haczyk67
9.12.2009, 18:17:17
Ok w takim razie robię pierwszy projekt.
@thek pewnie dobrze mówisz ale niestety nie rozumiem z tego nic ;-)
Aha i jeszcze co do tego zapytania to w pierwqszym projektcie bedzie działać to co podałeś?
Cytat
SELECT tag, COUNT(tag) AS tag_count FROM tbl_name GROUP BY tag ORDER BY tag_count DESC LIMIT 5;
To może wytłumaczę Ci algorytm i na jego podstawie wyłapiesz co jest grane...
1. Pobierz pole z tagami.
2. Zrób z nich tablicę tagów. (explode)
3. Usuń z tablicy duble. (array_unique)
4. Utwórz zapytanie sprawdzające czy dane tagi są w bazie i zwracające. (zapytanie do tabeli TAG oparte o kilka LIKE 'tag')
5. Zrób z wyników zapytania tablicę. (choćby w pętli)
6. Wykryj różnicę pomiędzy Twoimi tagami a zwróconymi przez bazę. (array_diff)
7. Jeśli są jakieś, to owe "dodatkowe" dodaj do bazy i poznaj ich id_tag. (tu można na kilka sposobów)
8. Jeśli to UPDATE to usuń z ProduktTag wszystkie rekordy tyczące produktu o danym id_produkt. (tu nawet nie ma co się rozpisywać - delete

)
9. Dodaj do bazy ProduktTag rekordy z danym id_produkt i uzyskanymi id_tagów. (tu zwykły insert)
To cały algorytm. Chyba już prościej się nie da. Następny krok to byłby gotowiec
Haczyk67
14.12.2009, 20:41:10
Użycie
Produkt (id, nazwa)
Tag (id, nazwa)
ProduktTag (id_produkt, id_tag) <--- tu znajduje sie troche duplikatów
strasznie wsyzstko komplikuje i powoduje że będę musiał
1) pobrać wszystkie tagi (+distinct)
2) pętlą wykonać x zapytan (count(tag))
3) porownac wszystkie tagi
4) pobrac 5 najpoularniejszych
Nie mogę użyć tego co podałeś
SELECT tag, COUNT(tag) AS tag_count FROM tbl_name GROUP BY tag ORDER BY tag_count DESC LIMIT 5;
Prosze o rozwiązanie jak pobrać 5 tagów bo już przebudowałem bazę tak jak radziłeś i nie wiem jak to rozwikłać mimo paru dni staran.
Crozin
14.12.2009, 21:43:40
Cytat
tu znajduje sie troche duplikatów
Tam nie ma prawa być żadnego duplikatu - powinno Ci wywalić wtedy błąd, bo powinieneś mieć PRIMARYKEY(id_produkt, id_tag).
Dlaczego nie możesz użyć tamtego zapytania? Jaki błąd wywala i jaka jest dokładna treść zapytania?
thek
15.12.2009, 10:23:00
Cytat(Haczyk67 @ 14.12.2009, 20:41:10 )

tu znajduje sie troche duplikatów
Tu nie może być żadnego duplikatu. Każda para jest unikalna. Widocznie starych par nie usuwałeś. Patrz na PUNKT 8 z mojego ostatniego posta.
Cytat(Haczyk67 @ 14.12.2009, 20:41:10 )

Nie mogę użyć tego co podałeś
SELECT tag, COUNT(tag) AS tag_count FROM tbl_name GROUP BY tag ORDER BY tag_count DESC LIMIT 5;
Więc włącz myślenie. Skoro masz nazwy tabel Produkt, Tag, ProduktTag o podanej strukturze, to zapytanie musisz do tego DOPASOWAĆ
1. Pogrupuj tagi z tabeli złączeniowej ProduktTag po id_tag i zrób count na tym polu.
2. By dodatkowo uzyskać nazwę-słowo. Połącz z tabelą Tag po jego id
3. Pole z nazwą (i ewentualnie id) tagu oraz ich ilość wrzuć do wyświetlanych kolumn.
Jaki efekt?
SELECT t.id, t.nazwa, count(z.id_tag) AS liczba FROM ProduktTag z LEFT JOIN Tag t ON z.id_tag = t.id GROUP BY z.id_tag ORDER BY liczba DESC LIMIT 5
Haczyk67
15.12.2009, 21:02:54
Dzięks, już śmiga
Niestety pojawił się kolejny problem. Musze miec ilosć wystapien danego tagu. Jedyne co mi przychodzi do glowy to zapisać wyniki podanego przez was zapytania w tablicy $abcdef[x]['tag_ID']['tag_count'], potem pętlą pobranie nazw tagów o tym ID i dalej niestety nie mam pomysłu jak połączyć te dane.
thek
16.12.2009, 08:54:17
Czy Ty właściwie sprawdziłeś co Ci podałem w wyniku? Zobacz CO Ci zwraca to zapytanie! masz ID tagu, ilość wystąpień i
nazwę. Masz wszystkie dane już. Dlatego użyłem LEFT JOIN z tabelą Tag, by ową nazwę wyciągnąć. Zrób sobie to zapytanie w PhpMyAdmin czy czego tam użuywasz i sprawdź dokładnie zamiast bezmyślnie przepisywać
Crozin
16.12.2009, 13:30:30
btw: nie ma sensu używać LEFT/RIGHT JOIN'a w takich przypadkach (gdy mamy pewność istnienia wszystkich relacji).
Haczyk67
16.12.2009, 15:06:56
Użyłem rozwiazania crozina, tego starego. Nie testowalem Twojego, ale już to naprawiam. Powiedzcie mi tylko czy mam w koncu użyc tego LEFT JOIN czy nie.
thek
16.12.2009, 19:05:47
LEFT/RIGTH określają "kierunek łączenia" tabel. Czy łączymy wszystkie rekordy z tej po lewej dopasowując wyniki z drugiej, czy wszystkie rekordy z tabeli po prawej dopasowując rekordy z pierwszej. W tym konkretnym przypadku to nie ma znaczenia, bo jest relacja 1 do 1 (konkretne id po obu stronach), ale w przypadku gdy jest wieloznaczność, to ma to już duże znaczenie. Poza tym w przypadku braku dopasowania domyślnie brakujące dane są uzupełniane NULLami. Są różne rodzaje łączeń i musisz po nich po prostu poczytać więcej by wiedzieć kiedy jakie stosować.
Haczyk67
19.12.2009, 12:29:16
Mam problem z Twoim zapytaniem
SELECT t
.id
, t
.nazwa
, count(z
.id_tag
) AS liczba FROM ProduktTag z LEFT
JOIN Tag t ON z
.id_tag
= t
.id GROUP BY z
.id_tag ORDER BY liczba DESC LIMIT
5
Mój układ bazy to
Produkt (id_produkt, nazwa_produkt)
Tag (id_tag, nazwa_tag)
ProduktTag (id_produkt, id_tag)
nie ma t.nazwa w tabeli produktTag, pole name znajduje się w tabeli Tag, tak więc Twoje zapytanie nie działa. Czy mógłbyś napisać mi to zapytanie zgodnie z nazwami w mojej bazie? Sam próbowałem ale wszystko co wpisuje kończy się błędem Mysql albo złymi danymi.
Crozin
19.12.2009, 14:15:02
1) Po co stosujesz suffix "_nazwa_tabeli" w kolumnach bazy danych? Jedynie nieczytelne się to wszystko robi.
2)
SELECT t.id, t.nazwa, count(pt.tag_id) AS cnt FROM ProuctTag pt JOIN Tag t ON pt.tag_id = t.id GROUP BY cnt ORDER BY cnt DESC LIMIT 5;
Haczyk67
20.12.2009, 08:36:54
Cytat
Can't group on 'cnt'
Taki błąd wywala.
Crozin
20.12.2009, 12:21:17
Grupowanie powinno być po t.id.
kallosz
20.12.2009, 12:50:52
Jak czytam ten temat to nie mogę ze śmiechu. Chłopaki się produkują a Pan Haczyk nawet nie chce głową ruszyć.
Jeśli nie masz takich nazw pól/kolumn to zmień na takie jakie masz to przecież aż takie ''trudne' nie jest.
No ludzie trochę głowa ruszyć - to nie boli.
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę
kliknij tutaj.