使用递归CTE解析组而不是层次结构 [英] Using recursive CTE to resolve a group, not hierarchy

查看:97
本文介绍了使用递归CTE解析组而不是层次结构的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用以下示例数据在SQL Server中进行递归CTE

 班级学生
------ ------
的英语Sally<-Sally正在寻找
的英语Peter<-彼得与Sally处于同一个阶级
瑞典语Peter< -因彼得在该课程上
荷兰人彼得而找到--因彼得在该课程上
芬兰哈利而找到--未找到,与班级或学生没有关系
瑞典语蒂姆<-发现因为彼得在瑞典语课堂上
西班牙文Lauren<-未找到,与班级或学生没有关系
西班牙文科林<-未找到,与班级或学生没有关系

所以我需要一个CTE,将'Sally'作为参数赋予它,它将找出与Sally相关的所有不同类,然后与Sally所在班级相关的学生,然后与Sally在同一班级中的所有学生相关的课程,依此类推



但是我只是无法弄清楚如何编写联接,这是我尝试过的尝试,但失败了:

 使用myCTE(班级,学生)作为

SELECT班级,学生在TABLE1中,TABLE1.Student ='Sally'
UNION ALL
从表1中选择t.Class,t.student t
在t2.Class = t.Class
上加入myCTE t2)
SELECT * FROM myCTE


解决方案

第一个问题是您拥有无限递归:Sally需要英语与彼得(英语)和萨莉(英语)一起学习,与彼得(萨利)和英语(英语)一起学习。 CTE。您目前正在加入来获得同一班的其他学生,但是您还需要加入学生为学生准备其他课程。



类似的方法应该起作用:

 与cteSource作为

SELECT
Class,
Student,
-为每条记录创建唯一的ID:
ROW_NUMBER( )OVER(按学生,班级顺序)作为ID

表1
),
cteRecursive(班级,学生,IDPath)作为

SELECT
类,
学生,
-用于排除我们已经访问过的记录:
Convert(varchar(max),'/'+ Convert(varchar(10), ID)+'/')
来自
cteSource
WHERE
Student ='Sally'

UNION ALL

- -同一班级的学生:
SELECT
T.Class,
T.Student,
R.IDPath + Convert(varchar(10),T.ID)+'/ '
来自
cteSource as T
INNER JOIN cteRecursive As R
ON T.Class = R.Class
WHERE
CharIndex('/'+ Convert(varchar(10), t.ID)+'/',R.IDPath)= 0

UNION ALL

-学生的其他班级:
选择
T.Class,
T.Student,
R.IDPath + Convert(varchar(10),T.ID)+'/'
FROM
cteSource as T
内部联接cteRecursive As R
T.Student = R.Student
WHERE
CharIndex('/'+ Convert(varchar(10),t.ID)+'/',R .IDPath)= 0

选择
班级,
学生,
IDPath
FROM
cteRecursive
;

使用测试数据,您将获得以下结果:

 英语Sally / 7 / 
英语Peter / 7/5 /
荷兰语Peter / 7/5/4 /
瑞典语彼得/ 7/5/6 /
瑞典蒂姆/ 7/5/6/8 /
荷兰彼得/ 7/5/6/4 /
瑞典彼得/ 7/5/4 / 6 /
瑞典语Tim / 7/5/4/6/8 /

如果您使用的是SQL 2008或更高版本,如果将 IDPath 设置为 HierarchyID ,可能会获得更好的性能,但是

编辑

您可能需要将最终选择内容更改为:

 选择区域
班级,
学生

cte递归

处理同一记录有多个路径的情况。例如,荷兰/彼得,瑞典/彼得和瑞典/蒂姆都出现两次。


I'm trying to do a recursive CTE in SQL Server with the following example data

Class        Student
------       ------
English      Sally   <- Sally is what were searching for
English      Peter   <- Peter's on same Class as Sally
Swedish      Peter   <- Found because Peter's on this class
Dutch        Peter   <- Found because Peter's on this class
Finnish      Harry   <- Not found, no relation to class or student
Swedish      Tim     <- Found because Peter's on Swedish class
Spanish      Lauren  <- Not found, no relation to class or student
Spanish      Colin   <- Not found, no relation to class or student

So I need a CTE, to which I give 'Sally' as parameter, and it will find out all different classes related to Sally, then all Students related to Classes that Sally is in, then all classes related to students in the same classes as Sally, and so on until no more rows found.

But I just cannot figure out how to write the joins, this is what I tried but failed miserably:

WITH myCTE (Class, Student) AS
(
    SELECT Class, Student FROM TABLE1 WHERE TABLE1.Student= 'Sally'
    UNION ALL
    SELECT t.Class, t.Student FROM TABLE1 t
    JOIN myCTE t2 ON t2.Class = t.Class
)
SELECT * FROM myCTE

解决方案

The first problem is that you've got infinite recursion: Sally takes English with Peter, who takes English with Sally, who takes English with Peter...

Once you've sorted that out, you'll need an additional query in your recursive CTE. You're currently joining on Class to get the other students in the same class, but you also need to join on Student to get the other classes for the students.

Something like this should work:

WITH cteSource As
(
   SELECT
      Class,
      Student,
      -- Create a unique ID for each record:
      ROW_NUMBER() OVER (ORDER BY Student, Class) As ID
   FROM
      TABLE1
),
cteRecursive (Class, Student, IDPath) As
(
   SELECT
      Class,
      Student,
      -- Used to exclude records we've already visited:
      Convert(varchar(max), '/' + Convert(varchar(10), ID) + '/')
   FROM
      cteSource
   WHERE
      Student = 'Sally'

   UNION ALL

   -- Students in the same class:
   SELECT
      T.Class,
      T.Student,
      R.IDPath + Convert(varchar(10), T.ID) + '/'
   FROM
      cteSource As T
      INNER JOIN cteRecursive As R
      ON T.Class = R.Class
   WHERE
      CharIndex('/' + Convert(varchar(10), t.ID) + '/', R.IDPath) = 0

   UNION ALL

   -- Other classes for the students:
   SELECT
      T.Class,
      T.Student,
      R.IDPath + Convert(varchar(10), T.ID) + '/'
   FROM
      cteSource As T
      INNER JOIN cteRecursive As R
      ON T.Student = R.Student
   WHERE
      CharIndex('/' + Convert(varchar(10), t.ID) + '/', R.IDPath) = 0
)
SELECT
   Class,
   Student,
   IDPath
FROM
   cteRecursive
;

With your test data, you'll get the following results:

English   Sally   /7/
English   Peter   /7/5/
Dutch     Peter   /7/5/4/
Swedish   Peter   /7/5/6/
Swedish   Tim     /7/5/6/8/
Dutch     Peter   /7/5/6/4/
Swedish   Peter   /7/5/4/6/
Swedish   Tim     /7/5/4/6/8/

If you're using SQL 2008 or higher, you might get better performance if you make the IDPath a HierarchyID, but you'd need to test with your real data.

EDIT
You might need to change the final select to:

SELECT DISTINCT
   Class,
   Student
FROM
   cteRecursive

to deal with cases where there are multiple paths to the same record. For example, "Dutch/Peter", "Swedish/Peter" and "Swedish/Tim" all appear twice.

这篇关于使用递归CTE解析组而不是层次结构的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆