嵌套连接在一个天真的树集上

时间:2012-10-04 13:23:28

标签: mysql sql tree hierarchy

假设我有这个例子的内容:

CREATE TABLE NaiveTable
{
    id BIGINT NOT NULL, 
    parentId BIGINT NULL,
    name VARCHAR(20) NULL,
    CONSTRAINT hierarchy FOREIGN KEY (parentId) REFERENCES NaiveTable(id)
    PRIMARY KEY (id)
}

作为注释,parentId是对NaiveTable的id的引用(如果我错过了确切的语法)。

数据在这些

的某些地方
+---------+----------+----------+
|   id    | parentId |  name    | 
+---------+----------+----------+
|   1     |   null   |  node1   |
+---------+----------+----------+
|   2     |     1    |  node2   |
+---------+----------+----------+
|   3     |     1    |  node3   |
+---------+----------+----------+
|   4     |     2    |  node4   |
+---------+----------+----------+

列名包含一些未分隔的标签。我正在寻找一种在MySQL表上构建SQL查询的方法,其中所有信息将被平铺并按层次排序,如下所示:

node 1, depth 0
node 2, depth 1
node 4, depth 2
node 3, depth 1

注意:我无法以任何方式修改数据库架构。我只能创建SQL查询。此外,我不能使用WITH关键字,因为MySQL不支持它。有没有办法进行这样的查询? 但是,深度2或更高的任何解决方案都被视为足够好

编辑:如果你喜欢尝试,这里是SQL fiddle。)

2 个答案:

答案 0 :(得分:1)

如果数据库的模式是固定的并且您无法添加/编辑任何表,那么您所能做的就是在内存中构建树(使用某种编程语言),然后尝试计算每个节点在内存中的深度。所以我的答案是,只用一个查询就无法生成所需的输出!

但是,如果您可以修改数据库的架构,那么您可能需要检查this

答案 1 :(得分:0)

这是一个小的递归存储过程,它应该适用于任何深度。这是我的第一个存储过程,所以请让我知道如何改进它。

DROP PROCEDURE IF EXISTS tree_reader;
DELIMITER $$
CREATE PROCEDURE tree_reader(IN parent INT, IN depth INT) READS SQL DATA
BEGIN

  DECLARE id_val INT;
  DECLARE name_val VARCHAR(255);
  DECLARE _table_name VARCHAR(255);

  DECLARE no_more_rows BOOLEAN;
  DECLARE cur CURSOR FOR
      SELECT id, name
      FROM tree
      WHERE IF(parent IS NULL, parentid IS NULL, parentid = parent);

  DECLARE CONTINUE HANDLER FOR NOT FOUND SET no_more_rows = TRUE;

  -- create temporary table on outer call only --
  IF depth=0 THEN
    DROP TABLE IF EXISTS _tree;
    CREATE TEMPORARY TABLE _tree (id INT, name VARCHAR(255), depth INT);
  END IF;

  OPEN cur;

  -- loop with recursion --
  tree_loop: LOOP
    FETCH cur INTO id_val, name_val;
    IF no_more_rows THEN LEAVE tree_loop; END IF;
    INSERT INTO _tree VALUES (id_val, name_val, depth);
    CALL tree_reader(id_val, depth + 1);

  END LOOP tree_loop;

  CLOSE cur;

  -- output results on outer call only --
  IF depth=0 THEN
    SELECT * FROM _tree;
    DROP TABLE _tree;
  END IF;
END
$$
DELIMITER ;

一些注意事项:

  • 请致电程序:CALL tree_reader(NULL, 0);

  • 您可以使用任何父节点ID,但第二个参数始终为0。在实践中,我可能会添加一个不带参数的包装程序,并给出整个树。

  • 您必须设置递归限制才能正常工作:SET max_sp_recursion_depth = 6;(我随意选择了6个)。