4

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

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 不支持它。有没有办法进行这样的查询?但是,深度二或以上的任何解决方案 都被认为足够好

编辑:如果你喜欢实验,这里是SQL fiddle :)

4

2 回答 2

1

如果数据库的模式是固定的并且您不能添加/编辑任何表,您所能做的就是在内存中构建树(在某些编程语言中),然后尝试计算每个节点在内存中的深度。所以我的回答是,你不能只用一个查询来产生你想要的输出!

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

于 2012-10-04T13:34:04.100 回答
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)。

于 2012-10-04T14:45:21.947 回答