我有下表:
id | parent_id | searchable | value
--------------------------------------------
1 | 0 | 0 | a
2 | 1 | 0 | b
3 | 2 | 1 | c
4 | 0 | 0 | d
5 | 4 | 1 | e
6 | 0 | 0 | f
7 | 6 | 0 | g
8 | 6 | 0 | h
9 | 0 | 1 | i
我需要提取所有顶级记录(所以parent_id = 0
)。
但只有父母或其中一个孩子可以搜索的记录(searchable = 1
)
所以在这种情况下,输出应该是:
id | parent_id | searchable | value
--------------------------------------------
1 | 0 | 0 | a
4 | 0 | 0 | d
9 | 0 | 1 | i
因为这些都是顶级记录而且它是自己或他的一个孩子(无论可搜索的孩子的“深度”)都是可搜索的。
我正在使用MySQL。我不确定是否可以用一个查询来编写它,但我认为它应该用一段递归代码或函数来完成。
**注意:不知道这棵树有多“深”。
答案 0 :(得分:1)
您必须使用存储过程来执行此操作。
使用searchable = 1查找所有行,将它们的id和parent_id存储在临时表中。 然后执行自联接以将父项添加到此临时表中。 重复直到不能再添加行(显然更好,确保树不是循环的)。 最后你有一个表只有在树下某处有可搜索后代的行,所以只显示没有父级的行(在顶部)。
假设你的表名为'my_table',那么这个应该可以工作:
DELIMITER //
DROP PROCEDURE IF EXISTS top_level_parents//
CREATE PROCEDURE top_level_parents()
BEGIN
DECLARE found INT(11) DEFAULT 1;
DROP TABLE IF EXISTS parent_tree;
CREATE TABLE parent_tree (id int(11) PRIMARY KEY, p_id int(11)) ENGINE=HEAP;
INSERT INTO parent_tree
SELECT id, parent_id FROM my_table
WHERE searchable = 1;
SET found = ROW_COUNT();
WHILE found > 0 DO
INSERT IGNORE INTO parent_tree
SELECT p.id, p.parent_id FROM parent_tree c JOIN my_table p
WHERE p.id = c.p_id;
SET found = ROW_COUNT();
END WHILE;
SELECT id FROM parent_tree WHERE p_id = 0;
DROP TABLE parent_tree;
END;//
DELIMITER ;
然后只是叫它:
CALL top_level_parents();
将等于
SELECT id FROM my_table WHERE id_is_top_level_and_has_searchable_descendant
答案 1 :(得分:0)
可以在较新的Mysql中进行递归查询,当被问到时可能不在后面。
获取父母和子女的数据,其中顶级父母的名称为“ A”,“ B”或“ C”。
与MySQL 8.0兼容。 https://dev.mysql.com/doc/refman/8.0/en/with.html
第一部分获取父级顶层并对其进行过滤,第二部分使孩子加入其父级。
WITH RECURSIVE tree AS (
SELECT id,
name,
parent_id,
1 as level
FROM category
WHERE parent_id = 0 AND (name = 'A' or name = 'B' or name = 'C')
UNION ALL
SELECT c.id,
c.name,
c.parent_id,
t.level + 1
FROM category c
JOIN tree t ON c.parent_id = t.id
)
SELECT *
FROM tree;
要查找父级或其子级之一是否可搜索,可以使用COALESCE(NULLIF(p.searchable,0),NULLIF(c.searchable,0))并通过顶部拉入该值级别的父级ID,然后重新加入。
因此要初始化您的示例数据:
CREATE TABLE `category` (
`id` int(11) NOT NULL,
`parent_id` int(11) NULL DEFAULT NULL,
`searchable` int(11) NULL DEFAULT NULL,
`value` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
INSERT INTO category (id, parent_id, searchable, value) VALUES
(1,0,0,'a'),
(2,1,0,'b'),
(3,2,1,'c'),
(4,0,0,'d'),
(5,4,1,'e'),
(6,0,0,'f'),
(7,6,0,'g'),
(8,6,0,'h'),
(9,0,1,'i');
回答这个问题。
WITH RECURSIVE tree AS (
SELECT id,
value,
parent_id,
1 as level,
searchable,
id AS top_level_id
FROM category
WHERE parent_id = 0
UNION ALL
SELECT c.id,
c.value,
c.parent_id,
t.level + 1,
COALESCE(NULLIF(t.searchable,0), NULLIF(c.searchable,0)),
COALESCE(t.top_level_id) AS top_level_id
FROM category c
JOIN tree t ON c.parent_id = t.id
)
SELECT category.*
FROM category
LEFT JOIN tree ON tree.top_level_id = category.id
WHERE tree.searchable = 1;
注意:不处理循环链接。 如果有,则需要删除它们或对其进行约束,以免发生这种情况,或者以与可以带入顶层ID大致相同的方式添加访问列。