Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [MySQL]Ponowne podliczanie tagów wjednym zapytaniu.
Forum PHP.pl > Forum > Przedszkole
ficiek
Mam dwie tabele articles i tags:

Kod
    ARTICLES
    id
    tags
    content

    TAGS
    tag
    amount


Muszę przeliczyć ilość wszystkich tagów w tabeli tags. To nie powinno być trudne ponieważ korzystam z wyszukiwania pełnotekstowego w tabeli articles do wyszukiwania artykułów pod względem tagów. Tak więc możliwe jest coś w stylu:
  1. MATCH(articles.tags) AGAINST ('+tag' IN BOOLEAN MODE)


Próbowałem czegoś takiego, ale oczywiście bez efektu:
  1.  
  2. UPDATE tags
  3. SET tags.amount=COUNT(SELECT * FROM articles
  4. WHERE MATCH(articles.tags) AGAINST (CONCAT('+',tags.tag) IN BOOLEAN MODE)
jaslanin
Przeszukiwanie pełnotekstowe raczej nie nadaje się do tego. Jeżeli wystąpienia jakiegoś słowa przekraczają 50% ilości wszystkich słów jest ono ignorowane. Więcej info:

http://dev.mysql.com/doc/refman/5.0/en/ful...l-language.html

Cytat
A word that matches half of the rows in a table is less likely to locate relevant documents. In fact, it most likely finds plenty of irrelevant documents. We all know this happens far too often when we are trying to find something on the Internet with a search engine. It is with this reasoning that rows containing the word are assigned a low semantic value for the particular data set in which they occur. A given word may reach the 50% threshold in one data set but not another.

The 50% threshold has a significant implication when you first try full-text searching to see how it works: If you create a table and insert only one or two rows of text into it, every word in the text occurs in at least 50% of the rows. As a result, no search returns any results. Be sure to insert at least three rows, and preferably many more. Users who need to bypass the 50% limitation can use the boolean search mode; see Section 11.9.2, ?Boolean Full-Text Searches?.


Dodatkowo tagi muszą być oddzielone specjalnymi znakami, jak spacja, przecinek, kropka.

No i podstawowa sprawa, że baza danych jest źle zaprojektowana. Jest niezgodna z 1NF skoro musisz w ten sposób ją przeszukiwać.
ficiek
http://www.pui.ch/phred/archives/2005/05/t...l-fulltext.html
Przeczytałem ten artykuł, rozważyłem plusy i minusy, i stwierdziłem, że FULLTEXT to jednak najlepszy pomysł. Strona ma opierać się na tagach, ma być możliwość wyszukiwania wg jednego tagu, kilku itd. Drugi pomysł na jaki wpadłem to taki:
-tabela artykuły
-tabela tagi
-tabela łącząca artykuły z tagami (np. 5 tagów to 5 wpisów w tej tabeli)
Ale odrzuciłem ten pomysł ze względu na bardziej skomplikowane zapytania ( w mysql nie jestem ekspertem). W razie czego proszę o jakiś artykuł na ten temat albo opis całości, jestem otwarty na wszelkie zmiany.

Co do źle "zaprojektowanej bazy skoro musisz ja w ten sposób przeszukiwać". Mowa o FULLTEXT czy moim pytaniu? Jeśli o pytaniu, to podliczenie ma być awaryjne np. w razie usunięcia wszystkich starszych artykułów, wtedy ma być możliwość ponownego przeliczania ilości artykułów dla każdego z tagu. Natomiast to prawda, FULLTEXT ma wady w tym wypadku.

PS
FULLTEXT działa oczywiście na zasadzie boolean mode, więc nie ma problemu z limitem 50%.
Cytat
Users who need to bypass the 50% limitation can use the boolean search mode; see Section 11.9.2, ?Boolean Full-Text Searches?.

Właśnie ze względu na łatwe wyszukiwanie przez boolean mode wybrałem fulltext.
Crozin
Właśnie powinieneś mieć trzecią tabelę łączącą artykuły i tagi. Wtedy miałbyś relację wiele-do-wielu w najbardziej klasycznej, podstawowej formie do których silniki relacyjnych baz danych są najlepiej przystosowane. A zapytania byłby znacznie prostsze bo opierałby się na poprawnej strukturze danych.
ficiek
Taką strukturę tabel przyjąłem korzystając z prezentacji
Practical Full Text Search in MySQL (od 14 slajdu FULLTEXT, od 36 proponowany przez was inverted index - przedstawiony również jako dobra jak nie lepsza metoda)

Ale nie wiem czy zmienić sposób wyszukiwania bo jedynym problemem jest właśnie to... 'awaryjne' podliczenie tagów, a na to jest zapewne jakieś niezbyt skomplikowane zapytanie.
Crozin
Powinieneś mieć wspomnianą wcześniej strukturę jak podstawę. Możesz co najwyżej dodać kolumnę tagi, która będzie robiła za kopię na potrzeby wyszukiwania. Wtedy też dodajesz do tabeli z tagami kilka triggerów, które zajmować się będą aktualizacją tej kolumny w przypadku usunięcia tagu czy zmiany jego nazwy.
ficiek
Całkowicie mnie to dobiło, stwierdzam że rozwiązanie wiele-do wielu jest jednak dość skomplikowane. W dodatku nie widzę żadnego naprawdę dobrego tutoriala dotyczącego późniejszych zapytań, a jak patrzę na 2 czy 3 joiny to się trochę gubię. Jeśli ktoś zna jakiś dobry artykuł na ten temat to poproszę wink.gif.
Crozin
Nie za bardzo wiem co tutaj trzeba by tłumaczyć, ale http://www.google.pl/search?aq=f&sourc...ny+relationship
ficiek
Kod
ARTICLES
id
content

TAGS
id
tag

TAGSTOARTICLES
articleid
tagid

OK, przemyślałem i rozumiem, że przy takiej strukturze jak powyżej wybieralibyśmy np. takim zapytaniem, tak?
  1. SELECT A.*
  2. FROM tagstoarticles M, articles A, tags T
  3. WHERE M.tagid = T.id
  4. AND (T.tag IN ('tag', 'tag2', 'tag3'))
  5. AND A.id = M.articleid
  6. GROUP BY A.id
  7. HAVING COUNT( A.id )=3

Ale jak równocześnie pobrać wszystkie tagi danego artykułu żeby je wyświetlić? I jak to zrobić kiedy nie wyszukujemy po tagach tylko zwyczajnie wyświetlamy artykuły np wg daty?
Crozin
  1. SELECT a.id, a.content, t.tag FROM articles a INNER JOIN tagstoarticles tta ON a.id = tta.article_id INNER JOIN tags t ON t.id = tta.tag_id;
ficiek
Oto co wywalczyłem po długim chodzeniu po domu, mówieniu do monitora i wielu dziwnych poświęconych mi spojrzeniach:

Zwykłe wybranie artykułów:
  1. SELECT a.id, a.content, GROUP_CONCAT(t.tag) AS tags
  2. FROM articles a
  3. LEFT JOIN tagstoarticles tta ON tta.articleid = a.id
  4. LEFT JOIN tags t ON t.id = tta.tagid
  5. GROUP BY a.id

Wynik:
Kod
id   content          GROUP_CONCAT(t.tag)
1   Lorem ipsum 1    tag1,tag2
2   Lorem ipsum 2    tag1
3   Lorem ipsum 3    tag2


Wyszukiwanie po tagach (koniunkcja):

  1. SELECT a.id, a.content, GROUP_CONCAT(t.tag) AS tags
  2. FROM articles a
  3. LEFT JOIN tagstoarticles tta ON tta.articleid = a.id
  4. LEFT JOIN tags t ON t.id = tta.tagid
  5. WHERE a.id IN (SELECT A.id FROM tagstoarticles M, articles A, tags T
  6. WHERE M.tagid = T.id
  7. AND (T.tag IN ('tag1'))
  8. AND A.id = M.articleid
  9. GROUP BY A.id
  10. HAVING COUNT( A.id )=1
  11. )
  12. GROUP BY a.id

Wynik:
Kod
    id content         GROUP_CONCAT(t.tag)
    1   Lorem ipsum 1   tag1,tag2
    2   Lorem ipsum 2   tag1


Ale niestety drugie zapytanie to straszny bałagan, pewnie można prościej wink.gif.
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.