Funkcja GROUP_CONCAT()
Zapewne wielu z was (nas) miało nie raz styczność z poniższym zagadnieniem:
System aktualności z podziałem na kategorie, z założeniem, że jedna aktualność może należeć do wielu kategorii - np. system blogowy. Zakładając, że wiemy co to normalizacja bazy danych, robimy coś mniej więcej takiego:
- tabela `posts` (id, title, text)
- tabela `tags` (id, name)
- tabela `tags_to_posts` (post_id, tag_id)
Następnie, w celu wybrania postów i kategorii doń należących wywołujemy minimum 2 zaptytania:
Wybieramy wiadomości (zapytanie A):
SELECT * FROM `posts`
I w pętli wybieramy kategorie (zapytanie B):
SELECT t.name FROM `tags_to_posts` LEFT JOIN `tags` ON tags.id = tags_to_posts.tag_id WHERE tags_to_posts.post_id = ID_POSTU
Niby nic złego, i wiele osób zasugeruje takie właśnie rozwiązanie. Istnieje natomiast prostsze rozwiązanie, pozwalające nam wyciągnąć wszystkie interesujące nas informacje za jednym razem - jedyny warunek - MySQL >= 4.1
Powitajmy GROUP_CONCAT()
GROUP_CONCAT(nazwa_pola ORDER BY nazwa_pola SEPARATOR ',')
Przykład zastosowania (zapytanie C):
SELECT p.*, GROUP_CONCAT(t.name ORDER BY t.name SEPARATOR ', ' ) AS `kategorie` FROM `posts` AS `p` LEFT JOIN `tags_to_posts` AS `ttp` ON ttp.post_id = p.id LEFT JOIN `tags` AS `t` ON t.id = ttp.tag_id GROUP BY p.id
W wyniku zapytania otrzymamy wszystkie dane z tabeli `posts` ORAZ nową kolumnę o nazwie kategorie zawierającą wszystkie kategorie do których należy dany post oddzielone przecinkiem.
Co prawda zamiast dwóch prostych SELECTów zastosowaliśmy dwa LEFT JOINy - ale jeżeli przyjrzymy się bliżej, okazuje się, że zaoszczędziliśmy N zapytań, gdzie N to ilość pobranych przez zapytanie A aktualności (zapytanie B wykonywane jest w pętli dla każdego rekordu pobranego w zapytaniu A).
Więcej informacji (oraz przykładów zastosowania) znajdziesz w manualu MySQL.