我有一个MySQL表,如下所示:
id |
name |
parent_id |
19 |
category1 |
0 |
20 |
category2 |
19 |
21 |
category3 |
20 |
22 |
category4 |
21 |
... |
... |
... |
现在,我想有一个单一的MySQL查询,我只是提供id[例如说id=19],然后我应该得到它的所有子id[即结果应该有id '20,21,22']....
孩子们的等级尚不清楚;它可以变化....
我知道如何使用for循环…但是如何使用一个MySQL查询来实现相同的功能呢?
使用BlueM/tree php类在mysql中创建一个自关系表的树。
Tree和Tree\Node是PHP类,用于处理使用父ID引用分层结构的数据。一个典型的例子是关系数据库中的一个表,其中每个记录的“父”字段引用另一个记录的主键。当然,Tree不能只使用来自数据库的数据,而是使用任何数据:您提供数据,Tree使用它,而不管数据来自何处以及如何处理。阅读更多
下面是一个使用BlueM/tree的例子:
<?php
require '/path/to/vendor/autoload.php'; $db = new PDO(...); // Set up your database connection
$stm = $db->query('SELECT id, parent, title FROM tablename ORDER BY title');
$records = $stm->fetchAll(PDO::FETCH_ASSOC);
$tree = new BlueM\Tree($records);
...
如果需要快速读取速度,最好的选择是使用闭包表。闭包表为每个祖先/后代对包含一行。在你的例子中,闭包表是这样的
ancestor | descendant | depth
0 | 0 | 0
0 | 19 | 1
0 | 20 | 2
0 | 21 | 3
0 | 22 | 4
19 | 19 | 0
19 | 20 | 1
19 | 21 | 3
19 | 22 | 4
20 | 20 | 0
20 | 21 | 1
20 | 22 | 2
21 | 21 | 0
21 | 22 | 1
22 | 22 | 0
一旦有了这个表,分层查询就变得非常简单和快速。获取类别20的所有子类:
SELECT cat.* FROM categories_closure AS cl
INNER JOIN categories AS cat ON cat.id = cl.descendant
WHERE cl.ancestor = 20 AND cl.depth > 0
当然,无论何时使用这样的非规格化数据都有一个很大的缺点。您需要在类别表旁边维护闭包表。最好的方法可能是使用触发器,但是正确跟踪闭包表的插入/更新/删除有点复杂。与任何事情一样,您需要查看您的需求,并决定哪种方法最适合您。
编辑:请参阅问题:在关系数据库中存储层次数据的选项是什么?更多选项。不同的情况有不同的最佳解决方案。
这是一个分类表。
SELECT id,
NAME,
parent_category
FROM (SELECT * FROM category
ORDER BY parent_category, id) products_sorted,
(SELECT @pv := '2') initialisation
WHERE FIND_IN_SET(parent_category, @pv) > 0
AND @pv := CONCAT(@pv, ',', id)
输出:
我能想到的最好方法是
使用沿袭存储\排序\跟踪树。这已经足够了,而且阅读速度比其他任何方法都要快数千倍。
它还允许即使DB将改变也保持该模式(因为任何DB将允许使用该模式)
使用为特定ID确定谱系的函数。
您可以随心所欲地使用它(在选择中,或在CUD操作中,甚至按作业)。
谱系方法描述。可以在任何地方找到,例如
这里或者这里。
至于功能,这就是我的灵感所在。
在最后-得到或多或少简单,相对快速,简单的解决方案。
函数的身体
-- --------------------------------------------------------------------------------
-- Routine DDL
-- Note: comments before and after the routine body will not be stored by the server
-- --------------------------------------------------------------------------------
DELIMITER $$
CREATE DEFINER=`root`@`localhost` FUNCTION `get_lineage`(the_id INT) RETURNS text CHARSET utf8
READS SQL DATA
BEGIN
DECLARE v_rec INT DEFAULT 0;
DECLARE done INT DEFAULT FALSE;
DECLARE v_res text DEFAULT '';
DECLARE v_papa int;
DECLARE v_papa_papa int DEFAULT -1;
DECLARE csr CURSOR FOR
select _id,parent_id -- @n:=@n+1 as rownum,T1.*
from
(SELECT @r AS _id,
(SELECT @r := table_parent_id FROM table WHERE table_id = _id) AS parent_id,
@l := @l + 1 AS lvl
FROM
(SELECT @r := the_id, @l := 0,@n:=0) vars,
table m
WHERE @r <> 0
) T1
where T1.parent_id is not null
ORDER BY T1.lvl DESC;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
open csr;
read_loop: LOOP
fetch csr into v_papa,v_papa_papa;
SET v_rec = v_rec+1;
IF done THEN
LEAVE read_loop;
END IF;
-- add first
IF v_rec = 1 THEN
SET v_res = v_papa_papa;
END IF;
SET v_res = CONCAT(v_res,'-',v_papa);
END LOOP;
close csr;
return v_res;
END
然后你就
select get_lineage(the_id)
希望它能帮助到一些人:)
这是一个有点棘手的问题,检查一下它是否适合你
select a.id,if(a.parent = 0,@varw:=concat(a.id,','),@varw:=concat(a.id,',',@varw)) as list from (select * from recursivejoin order by if(parent=0,id,parent) asc) a left join recursivejoin b on (a.id = b.parent),(select @varw:='') as c having list like '%19,%';
SQL小提琴链接http://www.sqlfiddle.com/#!2 / e3cdf / 2
用字段名和表名替换。