我的生产数据与此处解释的邻接列表模型类似......
http://mysql.stu.edu.tw/tech-resources/articles/hierarchical-data.html
我的问题是如何知道嵌套有多深?在这种情况下,从最后一个叶子“Flash”到第一个节点“Electronics”是4。是否有返回此最大深度数的查询?
答案 0 :(得分:6)
MYSQL没有CTE也没有递归功能。您可以使用'经典循环' :
来解决它DELIMITER $$
DROP FUNCTION IF EXISTS figure_up_depth $$
--encapsulated as a function:
CREATE FUNCTION figure_up_depth(idToSearch INT) RETURNS INT BEGIN
--var to count steps from node to root:
DECLARE depth INT;
SET depth = 1;
--while we are not on root node:
WHILE ( idToSearch is not null) DO
SET idToSearch = ( select parent
from category
where category_id = idToSearch
);
SET depth = depth + 1;
END WHILE;
RETURN depth;
END;
$$
DELIMITER ;
<强>检查:强>
mysql> select figure_up_depth(7);
+--------------------+
| figure_up_depth(7) |
+--------------------+
| 4 |
+--------------------+
1 row in set (0.00 sec)
已编辑 2015年7月8日
我意识到OP要求 max 深度,而不是节点深度。一个简单的:
mysql> select max( figure_up_depth(category_id) ) as max_depth
from category;
如果表现重要,正确的解决方案是:
categories.parent
放入临时表table1
。categories.id = table1.id
将table1与类别相关联,将结果加入字段categories.parent
放在临时表table2
上。table1
并将table2
重命名为table1
。table1
有行,请转到第2步。循环次数迭代将是树深度:
DELIMITER $$
DROP PROCEDURE IF EXISTS letsgo;
CREATE PROCEDURE letsgo() BEGIN
DECLARE R int;
DECLARE D int;
SET D=0;
DROP TEMPORARY TABLE IF EXISTS children;
CREATE TEMPORARY TABLE children AS (SELECT category_id as id
FROM category
WHERE parent is NULL );
DROP TEMPORARY TABLE IF EXISTS children_prev;
CREATE TEMPORARY TABLE children_prev AS (SELECT * FROM children );
SET R = ( SELECT count(*) FROM children );
WHILE ( R > 0 ) DO
DROP TEMPORARY TABLE IF EXISTS children_aux;
CREATE TEMPORARY TABLE children_aux AS (
SELECT category_id as id
FROM category R
INNER JOIN children_prev C on C.id = R.parent
);
SET R = ( SELECT count(*) FROM children_aux );
INSERT INTO children
SELECT * FROM children_aux;
TRUNCATE TABLE children_prev;
INSERT INTO children_prev
SELECT * FROM children_aux;
SET D=D+1;
END WHILE;
SELECT D;
END;
$$
DELIMITER ;
测试:
mysql> call letsgo();
+------+
| D |
+------+
| 4 |
+------+
1 row in set (0.14 sec)
注意:抱歉脏解决方案,这是mysql:没有CTE,没有递归,没有选择函数递归,没有DO-While,...
答案 1 :(得分:2)
对于适用于有限“最大”深度的解决方案,您可以使用如下查询:
SELECT CASE
WHEN MAX(l8.category_id IS NOT NULL) THEN 8
WHEN MAX(l7.category_id IS NOT NULL) THEN 7
WHEN MAX(l6.category_id IS NOT NULL) THEN 6
WHEN MAX(l5.category_id IS NOT NULL) THEN 5
WHEN MAX(l4.category_id IS NOT NULL) THEN 4
WHEN MAX(l3.category_id IS NOT NULL) THEN 3
WHEN MAX(l2.category_id IS NOT NULL) THEN 2
WHEN MAX(l1.category_id IS NOT NULL) THEN 1
ELSE 0
END AS max_depth
FROM category l1
LEFT JOIN category l2 ON l2.parent = l1.category_id
LEFT JOIN category l3 ON l3.parent = l2.category_id
LEFT JOIN category l4 ON l4.parent = l3.category_id
LEFT JOIN category l5 ON l5.parent = l4.category_id
LEFT JOIN category l6 ON l6.parent = l5.category_id
LEFT JOIN category l7 ON l7.parent = l6.category_id
LEFT JOIN category l8 ON l8.parent = l7.category_id
WHERE l1.parent IS NULL
在这种情况下,如果“树”的深度超过八级,查询将返回8,即它检查的最大深度。
这可以扩展,但这种方法需要一些有限的最大值。 (MySQL对查询中的表引用数量有限制。)
树的“根”节点将具有父值的NULL值。所以,这就是级别1的节点。(可能有多个行具有NULL值,多个根。(我选择识别最大深度为1的一级树,如果没有,则选择返回0) root“node(s)。
表达式ln.category_id IS NOT NULL
如果为真则返回1,如果为假则返回0。如果有任何行是非空值,我们知道树至少有很多层。
CASE表达式基本上是检查:树是否至少有8级深度?如果是的话,我们已经完成了。如果没有,这棵树至少有7层深吗?如果没有,它至少是6级吗?等
如果删除CASE包装表达式和MAX
聚合,则返回的是树中的每个节点,并且它返回根节点。您可能希望从上到下排序......级别1,级别2,级别3等。
为了检查树的最大深度,我们想从下往上检查。
要检查某些无限深度,您需要递归,并且MySQL不支持SQL语句上下文中的任何内容。您将进入MySQL存储程序(过程或函数)的领域以获得递归解决方案。
(其他数据库为我们提供了方便的CONNECT BY
语法。但是,唉,MySQL没有。)
答案 2 :(得分:1)
这是关于它的好方法:)
begin transaction
CREATE TABLE #category(
category_id INT PRIMARY KEY,
name VARCHAR(20) NOT NULL,
parent INT DEFAULT NULL);
INSERT INTO #category
VALUES(1,'ELECTRONICS',NULL),(2,'TELEVISIONS',1),(3,'TUBE',2),
(4,'LCD',2),(5,'PLASMA',2),(6,'PORTABLE ELECTRONICS',1),
(7,'MP3 PLAYERS',6),(8,'FLASH',7),
(9,'CD PLAYERS',6),(10,'2 WAY RADIOS',6);
DECLARE @SQL NVARCHAR(MAX)
DECLARE @SQLF NVARCHAR(MAX)
DECLARE @SQLFINAL NVARCHAR(MAX)
DECLARE @CT as VARCHAR(10)
DECLARE @CTP as VARCHAR(10)
DECLARE @SQLFINALP as NVARCHAR(MAX)
SET @CT = 1
SET @CTP = 1
SET @SQL = 'Select t1.name ''lev1'' '
SET @SQLF = 'FROM #category t1 '
SET @SQLFINAL = @SQL + CHAR(13) + @SQLF + 'where t1.name = electronics and t1.name is not null'
While @@ROWCOUNT > 0
Begin
SET @SQLFINALP = @SQL + CHAR(13) + @SQLF + 'WHERE t1.name =''electronics'''
SET @CTP = @CT
Set @CT = @CT + 1
SET @SQL = @SQL +' , t' +@CT+'.name ''lev' + @CT + ''''
SET @SQLF = @SQLF + ' left join #category t' + @CT + ' on t' + @CTP + '.category_id = t' + @CT + '.parent '
SET @SQLFINAL = @SQL + 'into #temp' + CHAR(13) + @SQLF + 'where t' + @Ct + '.name is not null and t1.name = ''electronics'''
exec SP_EXECUTESQL @SQLFINAL
end
print @@ROWCOUNT
Select @CTP
print @SQLFINALp
exec SP_EXECUTESQL @SQLFINALp
rollback
答案 3 :(得分:1)
您可能需要考虑添加深度列。在层次结构中插入节点时,如果没有父节点,则深度为0,否则将1添加到父节点的深度。然后你可以直接选择它。这种方法非常有效,适用于任何深度