如何查询通过自引用多对多表连接的不同实体组?

时间:2013-03-31 00:48:45

标签: sql django data-modeling

试图弄清楚如何查询通过自引用多对多表连接的不同实体组。整个下午一直在戳它,以为我会问这里是否有其他人有想法。

例如,一个人有一群朋友,这些群组是独占的(即群组之间没有重叠 - 如果你愿意的话,还有一堆派系)。表结构可能如下所示:

person
| id | name |
| 1  | bob  |
| 2  | frank |
| 3  | chuck |
| 4  | nancy |
| 5  | alice |
| 6  | sally |

cliques
| from_person_id | to_person_id |
|       1        |      2       |
|       1        |      3       |
|       2        |      1       |
|       2        |      3       |
|       3        |      1       |
|       3        |      2       |
|       4        |      5       |
|       4        |      6       |
|       5        |      4       |
|       5        |      6       |
|       6        |      4       |
|       6        |      5       |

(Bob是Frank和Chuck的朋友,Frank是Bob和Chuck的朋友,Chuck是Bob和Frank的朋友等。)

我可以获得一堆与每个人的朋友相关的集合,但无法弄清楚如何将其煮沸。最终,我真正喜欢的是一个返回不同集团成员的查询,例如

| cliques |
| 1, 2, 3 |
| 4, 5, 6 |

但是,当然,除非我使用像group_concat(MySQL)或array_agg(PostgreSQL)这样的东西,否则SQL不会那样工作。我并不是严格反对这种方法,但我宁愿避免引入特定于后端的实现(我实际上使用的是Django的ORM,但不想分散这些细节)。

我的问题是:

  • 我试图以这种方式模拟事物,我是在吠叫错误的树吗?
  • 有没有办法组装不同的派系而不需要在调用代码中进行迭代?我不是要求每个clique有一行,因为这需要特定于db的聚合,但是可能是生成的id-per-clique和一组(clique_id,member_id)元组,然后我可以在调用代码中汇编它们?

1 个答案:

答案 0 :(得分:0)

如果您正在寻找连接的子图,这是一种方法。

您可以通过其中任何节点的最小ID来表征连接的子图。

从表开始存储子图ID,如:

create table subgraphids (
     personid int,
     subgraphid int
);

初始化:

insert into subgraphids(personid, subgraphid)
    select personid, min(subgraphid)
    from (select from_person_id as personid,
                 least(from_person_id, to_person_id) as subgraphid
          from cliques
          union all
          select to_person_id, least(from_person_id, to_person_id)
          from cliques
         ) t
    group by personid;

现在你有了暂定的子图。要更新它们,请使用类似的类型查询:

update subgraphid
    set subgraphid = (select min(s.subgraphid)
                      from cliques c join
                           subgraphid s
                           on c.from_person_id = s.personid or
                              c.to_person_id = s.personid
                      where subgraphid.personid = clique.from_person_id or
                            subgraphid.personid = click.to_person_id
                     );

重复此操作直到没有更新行。您可以明确检查该条件:

select count(*)
from subgraphid
where subgraphid > (select min(s.subgraphid)
                    from cliques c join
                         subgraphid s
                         on c.from_person_id = s.personid or
                            c.to_person_id = s.personid
                    where subgraphid.personid = clique.from_person_id or
                          subgraphid.personid = click.to_person_id
                   );

这将在原始图表中找到连接的子图。迭代需要在SQL之外的调用代码中进行。