2016-04-22 141 views
1

我有一个类树,它有两种结构:每个类都有一个路径和一个parentID。路径由类别的ID(从下到上)制成。 parentID引用另一个类别。所以我的桌子是这样的:颠倒树结构(MySQL)

id | name  | path | parentID 
---+-------------+---------+--------- 
1 | Root  | NULL | NULL 
2 | Main  | NULL | 1 
3 | Electronics | |2|  | 2 
4 | Computers | |3|2| | 3 
5 | PCs   | |4|3|2| | 4 
6 | Macs  | |4|3|2| | 4 
7 | Cameras  | |3|2| | 3 
8 | Canon  | |7|3|2| | 7 

现在我不需要类别'根'和'主'。我试图实现的是这样的输出:

id | resolved_path 
---+----------------------------- 
3 | Electronics 
4 | Electronics_Computers 
5 | Electronics_Computers_PCs 
6 | Electronics_Computers_Macs 
7 | Electronics_Cameras 
8 | Electronics_Cameras_Canon 

所以我有不同的深度,我需要的类别是相反的顺序。我没有真正发现这个在线。我得到的是这样的片段,显示该类别的深度:

SELECT 
*, 
(ROUND(
    (LENGTH(cat.path) - LENGTH(REPLACE(cat.path, '|', '')))/LENGTH('|') 
) - 2) depth 
FROM 
    categories cat 
WHERE 
    cat.path IS NOT NULL 

我不知道什么是简单:通过parentIDs去递归或做一些魔法的路径。

+1

你能适应这个答案以相反的顺序工作:http://stackoverflow.com/a/5928675/1503505 – Preuk

+0

@Preuk我的问题是我有不同的深度。所以我需要一些循环来解决这个问题。有没有办法做到这一点? – jkrzefski

+1

为什么不创建可用于搜索父类别的中间表(categories_parents)?它更快捷简单。 –

回答

0

你可以使用这个(不优化)功能作为基线:

DELIMITER // 
DROP FUNCTION IF EXISTS extract_path // 
CREATE FUNCTION extract_path(idlist VARCHAR(255)) 
RETURNS LONGTEXT 
BEGIN 
    DECLARE result LONGTEXT; 
    DECLARE tmplist VARCHAR(255); 
    DECLARE buffer VARCHAR(255); 
    DECLARE lastpos INT; 

    -- reverse and trim last separator (that first of reversed string) 
    SELECT TRIM(BOTH FROM SUBSTRING(REVERSE(idlist), 2)) INTO tmplist; 

    mainloop: LOOP 
     -- split on separator 
     SELECT LOCATE('|', tmplist) INTO lastpos; 
     -- detect end 
     IF lastpos IS NULL OR lastpos < 2 
     THEN LEAVE mainloop; 
     END IF; 

     -- resolve next id 
     SELECT cat.name INTO buffer 
     FROM categories cat 
     WHERE cat.id = TRIM(REVERSE(SUBSTRING(tmplist, 1, lastpos - 1))); 

     -- append new element 
     SELECT CONCAT(COALESCE(CONCAT(result, '_'), ''), buffer) INTO result; 

     -- prepare for next iteration 
     SELECT TRIM(BOTH FROM SUBSTRING(tmplist, lastpos + 1)) INTO tmplist; 

     -- detect end (corner case) 
     IF tmplist IS NULL 
     THEN LEAVE mainloop; 
     END IF; 

    END LOOP; 

    RETURN result; 
END // 

SELECT extract_path('|1|2|3|'); 
-- with categories 1 -> foo ; 2 -> bar ; 3 -> baz 
-- output is 'baz_bar_foo' 
DELIMITER ; 
+0

@jkrzefski没问题,只是问你是否需要澄清。无论如何,我建议你考虑修复你的模型,Juan Lago评论道。你可以从我的函数中提取元素来构建你的中间表。 – Preuk