mysql递归自连接相交列

时间:2016-07-10 03:20:26

标签: mysql join recursion intersection self

说我有一个起始表(派生)就像这样......

-------------------------------------------------------
| 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                          |
-------------------------------------------------------

基本上对父ID进行递归查找,然后在PermissionID列中交叉(内部连接)值。因此,孩子永远不会拥有父母的更多权限。

我已经在用户定义的函数上查找了一些东西(我认为我可以在一个列周围包裹一个udf并让它基于父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 ;

2 个答案:

答案 0 :(得分:1)

不可能在一个查询中执行此操作(除非在某些特殊情况下),但您可以重复清理查询以此方式执行递归。

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

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 permissions: usergroupID | permissionID

table tree: usergroupID | parentID 

在您当前的表格中,您在表格中多次获得相同的信息(parentIDusergroupID的父级的信息),也称为非规范化。这样做的一个实际结果是,对于相同的parentID,您可以有两个不同的usergroupID,这通常在树中是未定义的。

答案 1 :(得分:0)

好的,所以这不一定是答案......但在下面的设置中,我发现我正在获得正确的表格信息。这个例子在我知道的假设下工作(并且可以传递最大深度和分组关系(我相信我可以,虽然当我实际尝试使用&#34时分组关系可能是一个问题) ;真实的"数据)。无论如何,这就是我所拥有的

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; 

我仍然没有对此作为一个解决方案,因为我需要将所有已知的东西传递给存储过程以自动执行此操作。我只是通过左边连接到树深来实现真正的接近但是在这种情况下我得到一个带有一些列集的表,我需要以某种方式使用quazi-pivot。例如。

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;

这最终给了我所有的信息,但它只是一种我认为我无法使用的格式。它给了我这样一张桌子......

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