Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Tworzenie drzewka kategorii jednym zapytaniem
Forum PHP.pl > Forum > Bazy danych > MySQL
thomson89
Witam!

Chciałbym utworzyć takie drzewko kategorii:


Jednym zapytaniem, na podstawie takiej tabelki:


Niestety kompletnie nie wiem jak się za to zabrać.

Obecnie, robię to poziomami. Łączę się z bazą, zapytanie pobieram kategorie 1-szego poziomu. Sprawdzam czy są kategorie które mają id tej kategorii w polu id_kategori_wyzszej... itd.
WebSee
Pobierzesz, to jednym zapytaniem, tylko musisz odpowiednio posortować, w Twoim przypadku, musisz to zrobić tak, by one kategorie ułozyły się w takiej koleności, w jakiej będą wyświetlane (sortowanie po id, poziom, id_kategori_wyższej)

A póżniej tylko odpowiedni kod w PHP, który sprawdza czy dana kategoria jest kategorią główną lub należy do podkategorii

Mój kod (w nim miałem tylko kategorie i podkategorie), ale może Ci się przyda:
  1. $go = mysql_query ('SELECT * FROM category ORDER BY cid, catid ');
  2. while ($category = mysql_fetch_assoc($go)) {
  3. if ($cid == $category['catid']) { //jeżeli dana kategoria, nalezy do podkategorii
  4. echo '<li class="down"><a href="">'.$category['name'].'</a></li>';
  5. }
  6. else { //jeżeli jest to kategoria główna
  7. echo '<li>'.$category['name'].'</li>';
  8. $cid = $category['cid']; //wtedy przypisujesz do zmiennej jej id, aby póżniej sprawdzić, czy następne kategorie do niej należą czy nie, jeżeli nie należą, to oznacza że kolejna kategoria jest główna i znów to samo
  9. }
  10. }
Mchl
Jednym uniwersalnym zapytaniem nie da się. Drzewa słabo pasują do relacyjnych baz danych.

Rzuć sobie okiem na tą prezentację:
http://www.slideshare.net/billkarwin/sql-a...rns-strike-back

Jedna z części jest właśnie temu poświęcona.

[dodane]

Albo można jak kolega wyżej, wyciągnąć wszystko z tabeli i układać sobie w PHP.
zegarek84
można to posortować i poukładać jednym zapytaniem...
luknij na ten temat:
[SQLite]Pobieranie danych z trzech tabeli - chodziło tutaj o podkategorie - to nic, że masz w jednej tabeli winksmiley.jpg , ale przy jednym zapytaniu jest taki problem, że z góry musisz założyć jakie jest maxymalne zagłębienie (ile tych joinów)...
___________________________________
ograniczeń i mniej problemów możesz mieć jeśli zrobisz to tak jak radzi WebSee - czyli pobierz jednym zapytaniem zawartość całej tabeli z kategoriami do skryptu, po czym by dowolnie zagłębioną strukturę wykonać pasuje to wrzucić rekurencyjnie (akurat tak na szybko na tablicach nie mam pomysłu, ale..)... pierwszy pomysł jaki mi przychodzi do głowy to zrobić to obiektowo (kategoria jako obiekt)....

więc jako, że tyle obiektów co kategorii i dosyć łatwo odwołać się do id to zrobisz stos obiektów w tablicy o indeksach id, dalej obiekt powinien mieć zmienną składową stosu swoich podkategorii o których nie musisz na razie myśleć, metodę do wrzucania podkategorii... obiekty są przekazywane przez referencję więc wszystko ładnie pięknie... jak masz te obiekty sprawdzasz do jakiej kategorii obiekt np. 5 powinien należeć wg. powyższego do 3, więc piszesz:
stos[3]->ad(stos[5]);
i tak do końca...

przy tym wszystkim w zasadzie nie potrzebujesz informacji o poziomie zagłębienia oprócz jednej: jakie są kategorie główne...
mając to info zaczynasz od kategorii głównych, odpowiednią metodą pobierasz podkategorie (i dalej z podkategorii metodą podkategorie...)...

to by było chyba na tyle winksmiley.jpg - jakoś na tablicach nie mam ale na pewno trzeba by to zrobić z referencją winksmiley.jpg - zapewne algorytm będzie podobny jak z obiektami...
wookieb
Do tego typu zadań idealne jest drzewo left right, Poczytaj o nim.
m44
Jednakże wadą metody z left + right jest to, że nie można nałożyć ograniczenia górnego do określonej głębokości podczas pobierania drzewa lub jego fragmentu. Innym ciekawym rozwiązaniem metoda 5 opisana tutaj.
Crozin
Cytat
Jednakże wadą metody z left + right jest to, że nie można nałożyć ograniczenia górnego do określonej głębokości podczas pobierania drzewa lub jego fragmentu.
Można. Wystarczy model NestedSet rozszerzyć o dodatkową kolumnę depth, w której na sztywno jest zapisany poziom elementu w drzewie.
celbarowicz
wygląda na to, że trzy poziomy Tobie wystarczają napisz trzy zależne selekty i będzie OK STRUKTURA TABEL:
  1.  
  2. $sql="CREATE TABLE IF NOT EXISTS tabela_1(
  3. id_t1 int(9) NOT NULL auto_increment,
  4. nazwa_t1 varchar(30) NOT NULL ,
  5.  
  6. PRIMARY KEY klucz1 (id_t1)) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_polish_ci; ";
  7.  
  8. mysql_query($sql) or die(mysql_error());
  9. //-----------------------------------------------------------
  10. $sql="CREATE TABLE IF NOT EXISTS tabela_2(
  11. id_t2 int(9) NOT NULL auto_increment,
  12. id_t1_w_t2 int(9) NOT NULL,
  13. nazwa_t2 varchar(30) NOT NULL,
  14. PRIMARY KEY klucz2 (id_t2 ) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_polish_ci; ";
  15.  
  16. //-----------------------------------------------------------
  17. $sql="CREATE TABLE IF NOT EXISTS tabela_3(
  18. id_t3 int(9) NOT NULL auto_increment,
  19. id_t2_w_t3 int(9) NOT NULL,
  20. nazwa_t3 varchar(30) NOT NULL,
  21. PRIMARY KEY klucz3 (id_t3 ) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_polish_ci; ";
  22.  
  23. //-----------------------------------------------------------
  24. $sql="CREATE TABLE IF NOT EXISTS tabela_4(
  25. id_t4 int(9) NOT NULL auto_increment,
  26. id_t3_w_t4 int(9) NOT NULL,
  27. opis_fotki varchar(255) NOT NULL,
  28. nazwa_fotki varchar(255) NOT NULL,
  29. wyswietlanie varchar(1) NOT NULL,
  30.  
  31.  
  32. PRIMARY KEY klucz3 (id_t4 ) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_polish_ci; ";
  33.  
  34.  
  35.  
  36.  
  37.  
  38.  
  39.  
  40.  
  41.  

PATRZ poznam.yoyo.pl na razie ze względu na brak czasu umieściłem jedną galerię kraj->pPolska->szczecin,
icetique
Albo zainteresuj się doctrine. smile.gif

http://www.doctrine-project.org/documentat...data#nested-set
Crozin
@icetique: Jak jest związek pomiędzy strukturą drzewa, a ORMem?
icetique
Ślepy? Doctrine obsługuje drzewa...

Nie wiem, czy zauważyłeś, ale autor nie pyta stricte o drzewo, ale o zapytanie, a takowe może "wygenerować" używając ORMa i mając odpowiednią srukturę bazy! Na prawdę tego nie widać?
Crozin
- Chcę odsłuchać sobie płytę CD, jak to zrobić?
- Kup sobie samochód - ma wbudowane radio z odtwarzaczem CD.
icetique
Raczej tak bym to ujął:

- Chcę odsłuchać sobie płytę CD, jak to zrobić?
- Możesz kupić sobie sprzęt Hi-Fi, będziesz mógł odsłuchać płytę CD, dodatkowo masz equalizer, radio, co pewnie Ci się przyda.

Umila życie i jeśli ułatwia korzystanie z drzew w bazie, to ułatwia także inne rzeczy. I nie mówię, że MUSI zrobić to w doctrine, mówię, żeby się tym zainteresował, bo część innych funkcji Doctrine, może mu się kiedyś przydać, tak samo jak w Hi-Fi radio, pomimo, że potrzebujemy płyty CD.

Porównanie do samochodu jest po części trafne, ale oczywiście nie to miałem na myśli.
aio
No to żeby nie było sporu o to czy kupować samochód czy nie:

  1. /**
  2. * @link explainextended.com/2009/03/17/hierarchical-queries-in-mysql/
  3. */
  4. DELIMITER //
  5. DROP FUNCTION IF EXISTS hierarchy_crawler //
  6. CREATE FUNCTION hierarchy_crawler(value INT) RETURNS INT
  7. NOT DETERMINISTIC
  8. READS SQL DATA
  9. BEGIN
  10. DECLARE _id INT;
  11. DECLARE _parent INT;
  12. DECLARE _next INT;
  13. DECLARE CONTINUE HANDLER FOR NOT FOUND SET @id = NULL;
  14.  
  15. SET _parent = @id;
  16. SET _id = -1;
  17.  
  18. IF @id IS NULL THEN
  19. RETURN NULL;
  20. END IF;
  21.  
  22. LOOP
  23. SELECT MIN(id)
  24. INTO @id
  25. FROM nodes
  26. WHERE parent = _parent
  27. AND id > _id;
  28. IF @id IS NOT NULL OR _parent = @start_with THEN
  29. SET @level = @level + 1;
  30. RETURN @id;
  31. END IF;
  32. SET @level := @level - 1;
  33. SELECT id, parent
  34. INTO _id, _parent
  35. FROM nodes
  36. WHERE id = _parent;
  37. END LOOP;
  38. END //
  39. DELIMITER ;
  40.  
  41. SET @level:=0;
  42. SELECT CONCAT(REPEAT(' ', level - 1), CAST(hi.id AS CHAR)) AS treeitem, nazwa, parent, level
  43. FROM (
  44. SELECT hierarchy_crawler(id) AS id, @level AS level
  45. FROM (
  46. SELECT @start_with := 0,
  47. @id := @start_with,
  48. @level := 0
  49. ) vars, nodes
  50. WHERE @id IS NOT NULL
  51. ) ho
  52. JOIN nodes hi
  53. ON hi.id = ho.id


- `nodes` to nazwa Twojej tabeli z kolumnami: id, nazwa, parent (Twoje id_kategorii_wyzej), ...
- jak się przyjrzysz to w tabeli nie potrzebujesz nawet kolumny `poziom`! (tworzone automatycznie jako efekt uboczny - level)

Źródło: http://explainextended.com/2009/03/17/hier...eries-in-mysql/



Mchl
Funkcja fajna, ale trudno to nazwać 'jednym zapytaniem' winksmiley.jpg
wookieb
Panienki przecież podano rozwiązanie. Drzewo left right z parametrem depth i wsio. Po co jest dalsza dyskusja?
aio
Cytat(wookieb @ 25.03.2010, 15:32:19 ) *
Panienki przecież podano rozwiązanie. Drzewo left right z parametrem depth i wsio. Po co jest dalsza dyskusja?

np. żeby się nie zastanawiać co jest left a co jest right, Panienko?

Cytat(Mchl @ 25.03.2010, 15:27:46 ) *
Funkcja fajna, ale trudno to nazwać 'jednym zapytaniem' winksmiley.jpg

no ok, więc odpowiadając stricte na pytanie postawione w pierwszym poście:
  1. SET @H:='', @P:=0;
  2. SELECT *
  3. FROM nodes T
  4. WHERE (@H:=T.id)
  5. AND (@P:=T.parent)
  6. AND (
  7. SELECT COUNT((@H:=CONCAT(wT.id,',',@H)) | (@P:=wT.parent))
  8. FROM (
  9. SELECT *
  10. FROM nodes
  11. ORDER BY poziom DESC
  12. ) wT
  13. WHERE @P=wT.id
  14. )
  15. OR @P=0
  16. ORDER BY @H
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.