【问题标题】:Find exactly matched groups in a many to many relationship table在多对多关系表中查找完全匹配的组
【发布时间】:2016-05-07 15:54:44
【问题描述】:

下表显示了课程和学生之间的多对多关系。

    CREATE Table CourseStudents
        (
          CourseId INT NOT NULL,
         StudentId INT NOT NULL,
         PRIMARY KEY (CourseId, StudentId)
        );
 
 INSERT INTO CourseStudents VALUES (1, 1), (1, 2), (2, 1), (2, 2), (3, 3), (3, 2), 
 (4, 3), (4, 2), (5, 1)

示例数据

| CourseId | StudentId |
|----------|-----------|
|        1 |         1 |
|        1 |         2 |
|        2 |         1 |
|        2 |         2 |
|        3 |         2 |
|        3 |         3 |
|        4 |         2 |
|        4 |         3 |
|        5 |         1 |

我正在寻找一个查询,该查询返回具有完全相同学生的所有课程。我能够提出如下所示的查询。

WITH CourseGroups AS
(
SELECT c.CourseId,
STUFF ((
SELECT ',' + CAST(c2.StudentId AS VARCHAR)
  FROM CourseStudents c2
  WHERE c2.CourseId = c.CourseId
  ORDER BY c2.StudentId
  FOR XML PATH ('')), 1, 1, '') AS StudentList
FROM CourseStudents c
GROUP BY c.CourseId)
SELECT cg.StudentList,
STUFF ((
SELECT ',' + CAST(cg2.CourseId AS VARCHAR(10))
  FROM CourseGroups cg2
  WHERE cg2.StudentList = cg.StudentList
  FOR XML PATH ('')), 1, 1, '') AS ExactMatchCourseList
FROM CourseGroups cg
GROUP BY cg.StudentList
HAVING COUNT(*) > 1

查询返回

| StudentList | ExactMatchCourseList |
|-------------|----------------------|
|         1,2 |                  1,2 |
|         2,3 |                  3,4 |

上面的结果很好。但我只需要ExactMatchCourseList。 我正在处理的表有超过十亿行,所以我需要一个有效的查询,可以在几分钟的运行时间内找到任何匹配的课程。感谢任何帮助。 SqlFiddle

【问题讨论】:

    标签: sql sql-server tsql sql-server-2012 sql-server-2014


    【解决方案1】:

    这只会在您的 CourseStudents 表上运行 2 次,而不是您当前正在执行的 4 次。如果您在 CourseStudents 表上的 CourseId 上添加索引,则第一次运行将只是索引扫描。它也只为每门课程运行一次原始 STUFF,而不是为每个学生运行一次,然后按课程分组。我遗漏了最后的东西,我不确定你是否想要,或者它只是你计算它的副产品。


    CREATE TABLE #Course
    (
        CourseId INT NOT NULL PRIMARY KEY
    );
    
    INSERT INTO #Course
    SELECT CourseId
    FROM
    CourseStudents s
    GROUP BY
    CourseId
    ORDER BY
    CourseId;
    
    CREATE TABLE #CourseStudentList
    (
    CourseId INT NOT NULL PRIMARY KEY,
    StudentList VARCHAR(MAX) NOT NULL
    );
    
    INSERT INTO #CourseStudentList
    SELECT
    c.CourseId,
    STUFF ((
    SELECT ',' + CAST(c2.StudentId AS VARCHAR)
      FROM CourseStudents c2
      WHERE c2.CourseId = c.CourseId
      ORDER BY c2.StudentId
      FOR XML PATH ('')), 1, 1, '') AS StudentList
    FROM
    #Course c
    ORDER BY
    c.CourseId;
    
    SELECT *
    FROM
    (
        SELECT
        l.CourseId,
        l.StudentList,
        COUNT(*) OVER (PARTITION BY l.StudentList) AS [Count]
        FROM
        #CourseStudentList l
    ) l
    WHERE
    l.[Count] > 1
    ORDER BY
    l.StudentList;
    

    【讨论】:

    • 我将此标记为答案,因为我能够在可接受的时间内检索到重复的课程。但是,我必须修改最后一个查询以输出重复课程列表以及学生列表。谢谢。
    【解决方案2】:

    这将为您提供课程对列表,但如果您要获得三次(或更多),那么您最终会得到一些额外的结果。我没有时间进一步解决这个问题,但也许它为你指明了正确的方向:

    WITH CTE_CourseMatches AS (
        SELECT
            CS1.CourseId AS CourseId_1,
            CS2.CourseId AS CourseId_2,
            COUNT(*) AS cnt
        FROM
            CourseStudents CS1
        INNER JOIN CourseStudents CS2 ON CS2.StudentId = CS1.StudentId AND CS2.CourseId > CS1.CourseId
        GROUP BY
          CS1.CourseId,
          CS2.CourseId
     ),
     CTE_CourseCounts AS (SELECT CourseId, COUNT(*) AS cnt FROM CourseStudents GROUP BY CourseID)
     SELECT
         CM.CourseId_1,
         CM.CourseId_2
     FROM
         CTE_CourseMatches CM
     INNER JOIN CTE_CourseCounts CC1 ON CC1.CourseId = CM.CourseId_1 AND CC1.cnt = CM.cnt
     INNER JOIN CTE_CourseCounts CC2 ON CC2.CourseId = CM.CourseId_2 AND CC2.cnt = CM.cnt
    

    【讨论】:

    • 谢谢,试一试。对于超过几场比赛,结果集不断增长。但我可能会想办法解决这个问题。
    猜你喜欢
    • 2020-01-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-01
    • 1970-01-01
    • 2017-08-22
    • 2010-11-25
    • 1970-01-01
    相关资源
    最近更新 更多