递归mysql选择?
我看到了,我希望他是不正确的,就像有人说主键在一列上是不正确的,我不能在多个列上设置主键一样 这是我的桌子递归mysql选择?,mysql,recursion,Mysql,Recursion,我看到了,我希望他是不正确的,就像有人说主键在一列上是不正确的,我不能在多个列上设置主键一样 这是我的桌子 create table Users(id INT primary key AUTO_INCREMENT, parent INT, name TEXT NOT NULL, FOREIGN KEY(parent) REFERENCES Users(id) ); +----+--------+---------+ | id | parent | name
create table Users(id INT primary key AUTO_INCREMENT,
parent INT,
name TEXT NOT NULL,
FOREIGN KEY(parent)
REFERENCES Users(id)
);
+----+--------+---------+
| id | parent | name |
+----+--------+---------+
| 1 | NULL | root |
| 2 | 1 | one |
| 3 | 1 | 1down |
| 4 | 2 | one_a |
| 5 | 4 | one_a_b |
+----+--------+---------+
我想选择userid2并递归,这样我就可以得到它的所有直接子级和间接子级(id4和5)
我该如何用这种方式写呢?我在postgresql和sqlserver中看到了递归
CREATE DEFINER = 'root'@'localhost'
PROCEDURE test.GetHierarchyUsers(IN StartKey INT)
BEGIN
-- prepare a hierarchy level variable
SET @hierlevel := 00000;
-- prepare a variable for total rows so we know when no more rows found
SET @lastRowCount := 0;
-- pre-drop temp table
DROP TABLE IF EXISTS MyHierarchy;
-- now, create it as the first level you want...
-- ie: a specific top level of all "no parent" entries
-- or parameterize the function and ask for a specific "ID".
-- add extra column as flag for next set of ID's to load into this.
CREATE TABLE MyHierarchy AS
SELECT U.ID
, U.Parent
, U.`name`
, 00 AS IDHierLevel
, 00 AS AlreadyProcessed
FROM
Users U
WHERE
U.ID = StartKey;
-- how many rows are we starting with at this tier level
-- START the cycle, only IF we found rows...
SET @lastRowCount := FOUND_ROWS();
-- we need to have a "key" for updates to be applied against,
-- otherwise our UPDATE statement will nag about an unsafe update command
CREATE INDEX MyHier_Idx1 ON MyHierarchy (IDHierLevel);
-- NOW, keep cycling through until we get no more records
WHILE @lastRowCount > 0
DO
UPDATE MyHierarchy
SET
AlreadyProcessed = 1
WHERE
IDHierLevel = @hierLevel;
-- NOW, load in all entries found from full-set NOT already processed
INSERT INTO MyHierarchy
SELECT DISTINCT U.ID
, U.Parent
, U.`name`
, @hierLevel + 1 AS IDHierLevel
, 0 AS AlreadyProcessed
FROM
MyHierarchy mh
JOIN Users U
ON mh.Parent = U.ID
WHERE
mh.IDHierLevel = @hierLevel;
-- preserve latest count of records accounted for from above query
-- now, how many acrual rows DID we insert from the select query
SET @lastRowCount := ROW_COUNT();
-- only mark the LOWER level we just joined against as processed,
-- and NOT the new records we just inserted
UPDATE MyHierarchy
SET
AlreadyProcessed = 1
WHERE
IDHierLevel = @hierLevel;
-- now, update the hierarchy level
SET @hierLevel := @hierLevel + 1;
END WHILE;
-- return the final set now
SELECT *
FROM
MyHierarchy;
-- and we can clean-up after the query of data has been selected / returned.
-- drop table if exists MyHierarchy;
END
它可能看起来很麻烦,但要使用它,请执行以下操作:
call GetHierarchyUsers( 5 );
(或您想要查找层次结构树的任何键ID)
前提是从您正在使用的一个键开始。然后,以此为基础再次加入users表,但要基于第一个条目的父ID。一旦找到,请将临时表更新为在下一个周期中不再尝试加入该键。然后继续,直到找不到更多的“父”ID密钥
无论嵌套有多深,这都会将整个记录层次结构返回给父级。但是,如果您只想要最后一个父级,可以使用@hierlevel变量只返回添加文件中的最新一个,或者按顺序和限制1我知道上面可能有更好、更有效的答案,但是这个片段提供了一种稍微不同的方法,并提供了-祖先和子级 其思想是不断地将相对rowid插入到临时表中,然后获取一行以查找其相关行,并重复该操作,直到处理完所有行。查询可以优化为只使用一个临时表 下面是一个工作示例 用法:
CALL getChildren(2);
-- returns
id parent name
4 2 one_a
5 4 one_a_b
CALL getAncestors(5);
-- returns
id parent name
1 (null) root
2 1 one
4 2 one_a
比尔·卡温是对的。MySQL没有任何递归查询功能,就像它的
CTE
一样。但是递归的行为仍然可以被模拟<代码>:D我不认为您可以通过单个查询在MySQL中执行递归,但我已经通过存储过程执行了类似的父层次结构查询,这些存储过程一直在寻找父级,直到找不到更多的父项为止。。。但通过临时表完成,该临时表在完成时被删除。。。这对你有用吗?@DRapp:那可能是可以接受的。学习这两种方法都很有趣希望这是一个帮助::这是一个很好的解决方案,但是如果在第一次运行完成之前再次运行查询,那么临时MyHierarch表将被删除,并且第一次查询将失败。创建名称中带有时间戳的临时表,并在过程结束时(而不是开始时)将其删除,可以解决该问题。@andrewlorien,是的,这是正确的,但是您正在处理动态sql。另一种选择是使用每个连接和/或用户都是唯一的#诱人的名称(或##temp)表。这将防止从一个用户意外删除到另一个用户。
CALL getChildren(2);
-- returns
id parent name
4 2 one_a
5 4 one_a_b
CALL getAncestors(5);
-- returns
id parent name
1 (null) root
2 1 one
4 2 one_a