mysql 递归自连接相交列

mysql recursive self join intersecting columns

假设我有一个像这样的起始 table(派生的)...

-------------------------------------------------------
| UsergroupID | ParentID | PermissionIDs              |
-------------------------------------------------------
| 1           | 0        | 1                          |
| 1           | 0        | 2                          |
| 1           | 0        | 3                          |
| 1           | 0        | 4                          |
| 1           | 0        | 5                          |
| 1           | 0        | 6                          |
| 2           | 1        | 1                          |
| 2           | 1        | 8                          |
| 2           | 1        | 9                          |
| 3           | 1        | 3                          |
| 3           | 1        | 8                          |
| 3           | 1        | 2                          |
-------------------------------------------------------

我希望获得如下所示的最终结果集

-------------------------------------------------------
| UsergroupID | ParentID | PermissionID               |
-------------------------------------------------------
| 1           | 0        | 1                          |
| 1           | 0        | 2                          |
| 1           | 0        | 3                          |
| 1           | 0        | 4                          |
| 1           | 0        | 5                          |
| 1           | 0        | 6                          |
| 2           | 1        | 1                          |
| 3           | 1        | 3                          |
| 3           | 1        | 2                          |
-------------------------------------------------------

这基本上是对 parent id 进行递归查找,然后交叉(内部连接)PermissionID 列中的值。所以 child 永远不会比 parent 拥有更多的权限。

我已经查找了有关用户定义函数的内容(我想我可以将一个 udf 包裹在一个列周围并让它根据 parent id 递归地相交)但这并没有让我走得太远。我真正能想到的唯一一件事不是在数据库端做,而是用服务器端代码做。

Solarflare -- 这是我刚刚使用您的脚本尝试的...这有效!

delimiter $$
CREATE PROCEDURE prcPermCleanup5()
BEGIN
DROP TABLE IF EXISTS table1;
CREATE TABLE table1 (usergroupID INT, parentID INT, StoreID INT)  ENGINE=MEMORY; 
INSERT INTO table1 VALUES
(1,0,1),
(1,0,2),
(1,0,3),
(1,0,4),
(2,1,1),
(2,1,2),
(2,0,5),
(3,2,2),
(3,2,7),
(4,1,1),
(4,1,2),
(5,4,1),
(5,4,8),
(6,2,1),
(6,2,6);
  REPEAT
     DELETE entry.*
    FROM table1 entry
    LEFT JOIN table1 parent
    ON entry.parentID = parent.usergroupID
    AND entry.`StoreID` = parent.StoreID
    WHERE parent.usergroupID IS NULL
    AND NOT entry.parentID = 0;
   UNTIL row_count() = 0 END REPEAT;  
  SELECT * FROM table1;
END $$
delimiter ;

不可能在单个查询中执行此操作(除非可能在某些特殊条件下),但您可以重复清理查询以这种方式进行递归。

如果您的清理工作是一次性的,您可以 运行 多次执行以下查询,直到不再有任何变化(您最多需要 depth of tree - 1 运行s):

delete entry.*
from table1 entry
left join table1 parent
on entry.parentID = parent.usergroupID
and entry.permissionIDs = parent.permissionIDs
where parent.usergroupID is null
and not entry.parentID = 0;

您可以在程序中自动执行该重复操作,例如

delimiter $$
create procedure prcPermCleanup()
begin
  repeat
    delete entry.*
    from table1 entry
    left join table1 parent
    on entry.parentID = parent.usergroupID
    and entry.permissionIDs = parent.permissionIDs
    where parent.usergroupID is null
    and not entry.parentID = 0;
  until row_count() = 0 end repeat;  
end $$
delimiter ;

call prcPermCleanup;

作为旁注:

您可能想要规范化您的数据,例如有一个单独的 table 用于您的权限:

table permissions: usergroupID | permissionID

table tree: usergroupID | parentID 

在你当前的 table 中,你在 table 中有多次相同的信息(parentIDusergroupID 的父级的信息),又名非规范化。一个实际的结果是,对于同一个 usergroupID,您可以有两个不同的 parentID,这通常在树中是未定义的。

好的,所以这不一定是答案...但在以下设置中,我发现我得到了正确的 table 信息。这个例子在我知道的假设下工作(并且可以传递最大深度和分组关系(我 相信 我可以,尽管当我实际尝试这个时分组关系可能是一个问题"real"数据)。反正我有这个

DROP TABLE IF EXISTS joshTestTable;
CREATE TABLE joshTestTable (id INT, parent INT, store INT) ENGINE=MEMORY; 
INSERT INTO joshTestTable VALUES
(1,0,1),
(1,0,2),
(1,0,3),
(1,0,4),
(2,1,1),
(2,1,2),
(2,0,5),
(3,2,2),
(3,2,7),
(4,1,1),
(4,1,2),
(5,4,1),
(5,4,8),
(6,2,1),
(6,2,6);


SELECT * FROM (
    SELECT * FROM joshTestTable p
    WHERE p.parent = 0
    UNION ALL
    SELECT c.* FROM joshTestTable p
    LEFT JOIN joshTestTable c 
    ON p.id = c.parent
    AND p.store = c.store
    AND c.parent=1
    UNION ALL
    SELECT c.* FROM joshTestTable p
    LEFT JOIN joshTestTable c 
    ON p.id = c.parent
    AND p.store = c.store
    AND c.parent IN (2,4) ) outter WHERE id IS NOT NULL; 

作为解决方案,我仍然不是很热衷于此,因为我需要将所有已知的东西传递给存储过程来自动执行此操作。我只是通过对树深度进行左连接来获得真正的接近,但在那种情况下,我得到一个 table 和一堆列集,我需要以某种方式进行准透视。例如.

SELECT * FROM joshTestTable p
LEFT JOIN joshTestTable c 
ON p.id = c.parent
AND p.store = c.store
LEFT JOIN joshTestTable gc
ON c.id = gc.parent
AND c.store = gc.store
WHERE p.parent = 0;

这最终为我提供了所有信息,但它的格式我认为我无法使用。它给了我这样的 table...

id  parent  store   id  parent  store   id  parent  store
1      0    1       2     1       1     6      2    1
1      0    1       4     1       1     5      4    1
1      0    2       2     1       2     3      2    2
1      0    2       4     1       2     NULL    NULL    NULL
1      0    3       NULL  NULL  NULL    NULL    NULL    NULL
1      0    4       NULL  NULL  NULL    NULL    NULL    NULL
2      0    5       NULL  NULL  NULL    NULL    NULL    NULL

最后我正在寻找这个(这是我的第一个查询确实给我的)...

id  parent  store
1   0       1
1   0       2
1   0       3
1   0       4
2   0       5
2   1       1
2   1       2
3   2       2
4   1       1
4   1       2
5   4       1
6   2       1