2011-11-24 10 views
15

Mam bazę danych MySQL przechowującą dane hierarchiczne przy użyciu metody tabeli zamykającej. Prosty przykładowy skrypt tworzenia bazy danych podąża za pytaniem. W tej chwili moim problemem jest jak wyciągnąć dane z bazy danych w prawidłowej kolejności? Obecnie używam poniższej instrukcji select.Hierarchiczna baza danych MySQL Closure Table - Jak wyciągnąć informacje we właściwej kolejności

SELECT `TreeData`.`iD`, `TreeData`.`subsectionOf`, 
     CONCAT(REPEAT('-', `TreePaths`.`len`),`TreeData`.`name`), 
     `TreePaths`.`len`,`TreePaths`.`ancestor`,`TreePaths`.`descendant` 
FROM `TreeData` 
LEFT JOIN `TreePaths` ON `TreeData`.`iD` = `TreePaths`.`descendant` 
WHERE `TreePaths`.`ancestor` = 1 
ORDER BY `TreeData`.`subsectionOrder` 

Wyciąga prawidłowe informacje, ale nie we właściwej kolejności.

Przykładowa baza danych tworzy skrypt z przykładowymi danymi.

-- Simple Sample 
SET FOREIGN_KEY_CHECKS=0; 
DROP TRIGGER IF EXISTS Tree_Insert; 
DROP TRIGGER IF EXISTS Tree_Update; 
DROP TABLE IF EXISTS TreePaths; 
DROP TABLE IF EXISTS TreeData; 
SET FOREIGN_KEY_CHECKS=1; 


CREATE TABLE `TreeData` (
    `iD`    INT NOT NULL,    -- PK 
    `subsectionOf` INT,      -- Parent ID & FK 
    `subsectionOrder` INT,      -- Oder of Subsections 
    `name`   NVARCHAR(500) NOT NULL, -- Name for the entry 
    PRIMARY KEY (`iD`), 
    FOREIGN KEY (`subsectionOf`) REFERENCES TreeData(`iD`) ON DELETE CASCADE, 
    INDEX(`name`) 
) ENGINE = MYISAM; 

-- Trigger to update the EntryPaths table for new entries 
DELIMITER // 
CREATE TRIGGER `Tree_Insert` AFTER INSERT ON `TreeData` FOR EACH ROW 
BEGIN 
    INSERT INTO `TreePaths` (`ancestor`, `descendant`, `len`) 
     SELECT `ancestor`, NEW.`iD`, len + 1 FROM `TreePaths` 
      WHERE `descendant` = NEW.`subsectionOf` 
      UNION ALL SELECT NEW.`iD`, NEW.`iD`, 0; 
END; // 
DELIMITER ; 


DELIMITER // 
CREATE TRIGGER `Tree_Update` BEFORE UPDATE ON `TreeData` FOR EACH ROW 
BEGIN 
    -- From http://www.mysqlperformanceblog.com/2011/02/14/moving-subtrees-in-closure-table/ 
    IF OLD.`subsectionOf` != NEW.`subsectionOf` THEN 
     -- Remove the node from its current parent 
     DELETE a FROM `TreePaths` AS a 
     JOIN `TreePaths` AS d ON a.`descendant` = d.`descendant` 
     LEFT JOIN `TreePaths` AS x 
     ON x.`ancestor` = d.`ancestor` AND x.`descendant` = a.`ancestor` 
     WHERE d.`ancestor` = OLD.`iD` AND x.`ancestor` IS NULL; 

     -- Add the node to its new parent 
     INSERT `TreePaths` (`ancestor`, `descendant`, `len`) 
     SELECT supertree.`ancestor`, subtree.`descendant`, supertree.`len`+subtree.`len`+1 
     FROM `TreePaths` AS supertree JOIN `TreePaths` AS subtree 
     WHERE subtree.`ancestor` = OLD.`iD` 
     AND supertree.`descendant` = NEW.`subsectionOf`; 
    END IF; 
END; // 
DELIMITER ; 


CREATE TABLE `TreePaths` (
    `ancestor`  INT NOT NULL, 
    `descendant` INT NOT NULL, 
    `len`   INT NOT NULL, 
    PRIMARY KEY (`ancestor`, `descendant`), 
    FOREIGN KEY (`ancestor`) REFERENCES TreeData(`iD`) ON DELETE CASCADE, 
    FOREIGN KEY (`descendant`) REFERENCES TreeData(`iD`) ON DELETE CASCADE 
) ENGINE = MYISAM; 

INSERT INTO `TreeData` VALUES(1, NULL, NULL, 'Root A'); 
INSERT INTO `TreeData` VALUES(2, 1, 1, 'Item 1'); 
INSERT INTO `TreeData` VALUES(3, 1, 2, 'Item 2'); 
INSERT INTO `TreeData` VALUES(4, 1, 3, 'Item 3'); 
INSERT INTO `TreeData` VALUES(5, 2, 2, 'Item 1 Sub Item 2'); 
INSERT INTO `TreeData` VALUES(6, 2, 1, 'Item 1 Sub Item 1'); 
INSERT INTO `TreeData` VALUES(7, 1, 3, 'Item 4'); 
INSERT INTO `TreeData` VALUES(8, 4, 1, 'Item 3 Sub Item 1'); 
INSERT INTO `TreeData` VALUES(9, 4, 2, 'Item 3 Sub Item 2'); 
INSERT INTO `TreeData` VALUES(10, NULL, NULL, 'Root B'); 
INSERT INTO `TreeData` VALUES(11, 10, 1, 'Item A'); 
INSERT INTO `TreeData` VALUES(12, 10, 2, 'Item B'); 
INSERT INTO `TreeData` VALUES(13, 10, 3, 'Item C'); 

Odpowiedz

21
SELECT d.`iD`, d.`subsectionOf`, 
     CONCAT(REPEAT('-', p.`len`), d.`name`) as hier, 
     p.`len`, p.`ancestor`, p.`descendant`, 
     GROUP_CONCAT(crumbs.`ancestor`) AS breadcrumbs 
FROM `TreeData` AS d 
JOIN `TreePaths` AS p ON d.`iD` = p.`descendant` 
JOIN `TreePaths` AS crumbs ON crumbs.`descendant` = p.`descendant` 
WHERE p.`ancestor` = 1 
GROUP BY d.`iD` 
ORDER BY breadcrumbs; 

+----+--------------+---------------------+-----+----------+------------+-------------+ 
| iD | subsectionOf | hier    | len | ancestor | descendant | breadcrumbs | 
+----+--------------+---------------------+-----+----------+------------+-------------+ 
| 1 |   NULL | Root A    | 0 |  1 |   1 | 1   | 
| 2 |   1 | -Item 1    | 1 |  1 |   2 | 1,2   | 
| 5 |   2 | --Item 1 Sub Item 2 | 2 |  1 |   5 | 1,2,5  | 
| 6 |   2 | --Item 1 Sub Item 1 | 2 |  1 |   6 | 1,2,6  | 
| 3 |   1 | -Item 2    | 1 |  1 |   3 | 1,3   | 
| 4 |   1 | -Item 3    | 1 |  1 |   4 | 1,4   | 
| 8 |   4 | --Item 3 Sub Item 1 | 2 |  1 |   8 | 1,4,8  | 
| 9 |   4 | --Item 3 Sub Item 2 | 2 |  1 |   9 | 1,4,9  | 
| 7 |   1 | -Item 4    | 1 |  1 |   7 | 1,7   | 
+----+--------------+---------------------+-----+----------+------------+-------------+ 
+4

Zadziała tylko jeśli drzewo zostało utworzone sekwencyjnie. Wyobraź sobie, że Twój element główny nie był równy 1, był 12, to już by nie działało. Przesuń kilka węzłów tutaj, aby usunąć sekwencję, a drukowanie zostanie przerwane. Ktoś ma inne rozwiązanie? –

+1

Jeśli twoja tabela zamknięcia zawiera również kolumnę 'pathlength', możesz użyć' GROUP_CONCAT (crumbs.ancestor ORDER BY pathlength) '. –

+1

@Thomas GROUP_CONCAT (DISTINCT crumbs.'ancestor' ORDER BY crumbs'ancestor') wykona zadanie, również – kukipei

Powiązane problemy