2016-01-29 3 views
0

Таблица, показанная ниже, отображает отношения между многими и многими между курсами и студентами.Найти сопоставленные группы во многих отношениях Таблица

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

ответ

0

Это делает только 2 пробегов над столом CourseStudents, вместо 4 вы находитесь в настоящее время делает. И если вы добавите индекс в CourseId в таблицу CourseStudents, первый прогон будет только сканированием индекса. Он также запускает только оригинальный STUFF один раз для каждого курса, а не один раз для каждого ученика, а затем группируется по курсу. Я оставил окончательный 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; 
+0

Я отмечаю это как ответ, поскольку мне удалось получить дубликаты курсов в течение приемлемого времени. Однако мне пришлось изменить последний запрос, чтобы вывести список повторяющихся курсов вместе со списком учеников. Благодарю. – ziddarth

0

Это даст вам список пар курсов, хотя, если вы собираетесь получать три раза (или больше), то вы получите дополнительные результаты. У меня нет времени, чтобы играть с этим дальше, чтобы исправить эту проблему, но, возможно, это указывает на вас в правильном направлении:

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

Спасибо, даст ему попробовать. Для более чем нескольких совпадений набор результатов продолжает расти. Но я, вероятно, смогу найти способ работать с этим. – ziddarth

Смежные вопросы