Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [php][mysql] Pobieranie najpopularniejszych tagów z MySQL
Forum PHP.pl > Forum > PHP
Haczyk67
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:

  1. 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
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
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
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
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
Mając nazwę zamiast ID strasznie sobie komplikujesz życie, bo powtarzasz masę danych w bazie.

  1. SELECT tag, COUNT(tag) AS tag_count FROM tbl_name GROUP BY tag ORDER BY tag_count DESC LIMIT 5;
Haczyk67
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
Tak, ale operowanie na liczbach jest nieporównywalnie lżejsze od męczenia się ze stringami.
Haczyk67
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
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.
thek
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 smile.gif 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
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;
thek
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 smile.gif )
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 winksmiley.jpg
Haczyk67
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
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
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?
  1. 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
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
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ć smile.gif
Crozin
btw: nie ma sensu używać LEFT/RIGHT JOIN'a w takich przypadkach (gdy mamy pewność istnienia wszystkich relacji).
Haczyk67
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
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
Mam problem z Twoim zapytaniem
  1. 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
1) Po co stosujesz suffix "_nazwa_tabeli" w kolumnach bazy danych? Jedynie nieczytelne się to wszystko robi.
2)
  1. 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
Cytat
Can't group on 'cnt'


Taki błąd wywala.
Crozin
Grupowanie powinno być po t.id.
kallosz
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.
Invision Power Board © 2001-2025 Invision Power Services, Inc.