Zagadnienie było z pozoru proste. Potrzebowałem za pomocą SQL-a wyciągać z bazy wszystkie podkategorie dla danej kategorii w sklepie. Typowe drzewo. Od razu przychodzi na myśl rozwiązanie: funkcja/procedura rekurencyjna… niestety w MySQL-u nie jest to takie proste…Nie pozwala nam on na ich Tworzenie. Po dłuższym czasie wpadłem na rozwiązanie, które zamieszczam poniżej. Nie jest ono zbytnio eleganckie. Oto ono:
DROP PROCEDURE IF EXISTS getTreeElements;
DELIMITER //
CREATE PROCEDURE getTreeElements(root INT(8))
BEGIN
DROP TABLE IF EXISTS TreeElementsTemporaryTable;
CREATE TABLE TreeElementsTemporaryTable (id BIGINT NOT NULL, depth INT NOT NULL, UNIQUE (id));SET @parent = root, @depth = 0;
INSERT INTO TreeElementsTemporaryTable VALUES (root, @depth);
mlp: LOOP
set @depth = @depth + 1;
SELECT COUNT(*) FROM TreeElementsTemporaryTable INTO @pre;INSERT INTO TreeElementsTemporaryTable
SELECT node.id, @depth FROM TreeElementsStorageTable AS node, TreeElementsTemporaryTable AS nn
WHERE node.parent_id = nn.id
ON DUPLICATE KEY UPDATE TreeElementsTemporaryTable.id = node.id;SELECT COUNT(*) FROM TreeElementsTemporaryTable INTO @post;
IF @post = @pre THEN LEAVE mlp; END IF;
END LOOP;SELECT id FROM TreeElementsTemporaryTable; #tutaj wstaw Twoją kwerende
DROP TABLE TreeElementsTemporaryTable;
END//
DELIMITER ;
Małe wyjaśnienie:
TreeElementsTemporaryTable – nazwa tabeli przechowującej tymczasowe wyniki. Niestety tabela ta nie może być tabelą tymczasową (temporary table), gdyż do takich tabel możemy tworzyć jedynie jedno połączenie w zapytaniu, a jak widać powyżej potrzebowałem ich więcej.
TreeElementsStorageTable – tabela z danymi drzewa. W moim wypadku była to tabela o takiej strukturze:
CREATE TABLE IF NOT EXISTS `TreeElementsStorageTable` (
`id` bigint(20) NOT NULL auto_increment,
`parent_id` bigint(20) NOT NULL default ‘0′,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
KEY `parent_id` (`parent_id`)
);
Rozwiązanie niezbyt eleganckie, które stwarza dużo nadmiarowego ruchu, ale po kolei. Na początku do tabeli tymczasowej dodaje id głównego elementu, a następnie w każdym obiegu pętli dodaje to tej tabeli id tych elementów, których id rodzica już się znajduje w tej tabeli. Za każdym razem porównuje ilość wpisów przed i po aktualizacji tabeli tymczasowej. Gdy te liczby są sobie równe, oznacza to że już nie dodałem żadnego nowego elementu, a więc dotarłem do końca drzewa.
Jeśli, ktoś ma lepszy pomysł na rozwiązanie tego problemu niech śmiało pisze w komentarzach. Do usłyszenia.