【问题标题】:Query to group fully recursive relationships in many-to-many junction table查询以对多对多联结表中的完全递归关系进行分组
【发布时间】:2019-03-01 10:02:28
【问题描述】:

抱歉,如果这是在某处重复,在这里搜索和网络似乎与我的问题有相似但不完全匹配,所以我决定发布。

我将其称为多对多关系中的完全递归分组。我已经尝试编写 joins 和 ctes 来做到这一点,但没有完全递归,我只能深入一层,而且我对尝试编写嵌套动态游标并不十分兴奋。

假设表达式可以从这个“联结”表中导出以找到不同的学生/班级,因此可以仅使用一组来总结问题。

从学生班级中选择 *

+-------------+----------+--------------+
| student_id  | class_id | group_number |
+-------------+----------+--------------+
| 1           | A        | null         |
| 1           | C        | null         |
| 2           | A        | null         |
| 2           | B        | null         |
| 2           | C        | null         |
| 3           | E        | null         |
| 4           | B        | null         |
| 4           | F        | null         |
+-------------+----------+--------------+ 

问题是如何通过递归关系为每个学生和每个班级填充组号。例如:如果 student_id 1 有 class_id A,那么其他学生有哪些 class_id A?对于其他学生,他们还有什么其他课程?对于每个其他课程,哪些其他学生有这些课程?然后继续遍历结果,直到找不到更多的依赖项。

所以在这个例子中,最终更新将只包含两个组,这样读取,因为没有其他学生有 class_id C 并且 student_id 3 没有其他类:

+-------------+----------+--------------+
| student_id  | class_id | group_number |
+-------------+----------+--------------+
| 1           | A        | 1            |
| 1           | C        | 1            |
| 2           | A        | 1            |
| 2           | B        | 1            |
| 2           | C        | 1            |
| 3           | E        | 2            |
| 4           | B        | 1            |
| 4           | F        | 1            |
+-------------+----------+--------------+ 

【问题讨论】:

标签: sql sql-server tsql


【解决方案1】:

这真的很棘手。它是一种图步行算法(并不难)。但是你必须定义图表。您可以使用自联接定义两个类之间的链接。然后可以将其用于递归 CTE。

所以,要获得等价类(这里有点意思),你可以这样做:

with cs as (
      select *
      from (values (1, 'A'), (1, 'C'), (2, 'A'), (2, 'B'), (2, 'C'), (3, 'E'), (4, 'B'), (4, 'F')) v(student, class)
     ),
     cc as (
      select distinct cs1.class as class1, cs2.class as class2
      from cs cs1 join
           cs cs2
           on cs1.student = cs2.student
     ),
     cte as (
      select cc.class1 as class, cc.class2 as grp, cast(',' + cc.class1 + ',' as varchar(max)) as grps
      from cc
      union all
      select cte.class, cc.class2, 
             cast(grps + cc.class2 + ',' as varchar(max)) as grps
      from cte join
           cc
           on cc.class1 = cte.grp and cte.grps not like '%,' + cc.class1 + ',%'
    )
select cte.class, min(cte.grp)
from cte
group by cte.class;

如果要将它们转换为数字:

select cte.class, min(cte.grp),
       dense_rank() over (order by min(cte_grp)) as group_number
from cte
group by cte.class;

【讨论】:

  • 谢谢@GordonLinoff 精彩的回答.. 会花我一个月的时间。您能否指出代码中的哪个 cte 表示“您必须定义图形”,只是为了澄清一下?
  • 另外,我更新了最终选择以交叉申请第一个 cte (cs) 以让学生回来。 SELECT cs2.student, cte2.class, cte2.group_number FROM ( select cte.class, --min(cte.grp) min_grp, dense_rank() over (order by min(cte.grp)) as group_number from cte group by cte.class) cte2 cross apply (SELECT * FROM cs) cs2 WHERE cs2.class = cte2.class order by student, cte2.class
  • @R.Mull 。 . . “图”是两个类之间的连接。如果两个班级共享一个学生,则它们是相连的。这就是cc。之后,逻辑是使用递归 CTE 的图行走逻辑。诚然,这部分仍然不明显,但它是 CTE 的更典型用法。
猜你喜欢
  • 2020-04-29
  • 2021-09-19
  • 2016-05-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-25
  • 1970-01-01
相关资源
最近更新 更多