【问题标题】:Retrieve top-level parent MySQL检索顶级父 MySQL
【发布时间】:2014-03-04 10:27:29
【问题描述】:

我有下表:

  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。我不确定是否可以只用一个查询来编写它,但我认为应该用一段递归代码或一个函数来完成。

** 注意:不知道树有多“深”。

【问题讨论】:

  • 我已阅读标记为正确答案的文章,但我没有在其中找到我的答案。对于我正在使用的这个应用,使用左右列的嵌套集合模型效率很低
  • 您需要更改数据模型、编写存储过程或在应用程序中进行递归。对于 MySQL 的有限功能,您别无选择。它可以不通过单个查询来完成,因为 MySQL 缺乏执行递归查询的能力

标签: mysql sql recursive-query


【解决方案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

【讨论】:

  • 正是我需要的!如果您不总是放下并创建它,它的运行速度非常快。
【解决方案2】:

递归查询可以在较新的 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 一样。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多