SQL Server: перебирать строки одной таблицы, сравнивая наборы результатов из двух других таблиц - PullRequest
4 голосов
/ 15 мая 2019

У меня есть 3 таблицы, которые выглядят примерно так:

Студенты

studentID    studentName
1            John
2            Jack
3            Jane

Предметы

subjectID    subjectName
1            maths
2            geography
3            history
4            physics

Performance

studentID    subject_passed    mark_obtained
1            maths             40
2            physics           50
1            geography         40 
3            maths             80
1            physics           40
2            maths             70
2            geography         40
1            history           30

Используя приведенный выше пример, я хочу выбрать всех студентов, которые прошли все предметы И у которых общее количество баллов превышает 120.

Это то, что я до сих пор пробовал;

  1. Используя CURSOR (и цикл while) для итерации по идентификаторам студентов, затем используйте EXCEPT, чтобы найти различия между двумя наборами результатов, как это
[declarations]
[set CURSOR]
[FETCH NEXT]
while @@FETCH_STATUS
BEGIN
    SELECT subject FROM Subjects
        EXCEPT
    SELECT subject FROM Performance WHERE studentId=@id 
    [FETCH NEXT]
END

Я столкнулся с двумя проблемами с этим подходом; Возвращает n (количество студентов) разных наборов результатов Я также не могу понять, как включить второе условие в SUM, учитывая, что агрегатные функции не разрешены в предложении WHERE.

  1. Я также пытался выбрать «все предметы» в списке и «все предметы, которые студент пропустил» в другой список и сравнить их вот так; select student from Students where (select string_agg(all subjects)) = (select string_agg(subject passed by student)) но я все еще сталкиваюсь с той же проблемой: не включить второе условие СУММ в целую смесь вещей.

Требуется отобрать студентов, которые сдали все предметы и набрали более 120 баллов Я ценю любые советы о том, как это сделать.

Ответы [ 2 ]

3 голосов
/ 15 мая 2019

Используйте CROSS JOIN, чтобы получить список каждого студента и каждого предмета, а затем LEFT JOIN в таблицу Performance.Затем вы можете использовать предложение HAVING, чтобы отфильтровать те, которые прошли все предметы, с общей оценкой более 120. Я отфильтрую тех, кто не прошел все, с условным подсчетом, где я считаю только те строки, где mark_obtained имеет значение NULL в таблице Performance (что, как я полагаю, произойдет, если нет строки для студента / предмета).

CREATE TABLE dbo.Student (StudentID int, StudentName varchar(5));
INSERT INTO dbo.Student (StudentID,
                         StudentName)
VALUES(1,'John'),
      (2,'Jack'),
      (3,'Jane');

CREATE TABLE dbo.Subject (SubjectID int, SubjectName varchar(10));
INSERT INTO dbo.Subject (SubjectID,
                         SubjectName)
VALUES(1,'maths'),
      (2,'geography'),
      (3,'history'),
      (4,'physics');

CREATE TABLE dbo.Performance (StudentID int, subject_passed varchar(10), mark_obtained int);
INSERT INTO dbo.Performance (StudentID,
                             subject_passed,
                             mark_obtained)
VALUES (1,'maths',40),
       (2,'physics',50),
       (1,'geography',40),
       (3,'maths',80),
       (1,'physics',40),
       (2,'maths',70),
       (2,'geography',40),
       (1,'history',30);

GO

SELECT St.StudentID,
       St.StudentName,
       SUM(P.mark_obtained) AS Marks_obtained
FROM dbo.Student St
     CROSS JOIN dbo.Subject Su
     LEFT JOIN dbo.Performance P ON St.StudentID = P.StudentID
                                AND Su.SubjectName = P.subject_passed --This should really be ID
GROUP BY St.StudentID,
         St.StudentName
HAVING COUNT(CASE WHEN P.mark_obtained IS NULL THEN 1 END) = 0
   AND SUM(P.mark_obtained) > 120

GO

DROP TABLE dbo.Performance;
DROP TABLE dbo.Subject;
DROP TABLE dbo.Student;

Это гораздо лучший метод, чем использованиеCURSOR.SQL Server выделяет методы, основанные на множестве, а не итеративные, и Курсор падает в последнем.

db <> fiddle

2 голосов
/ 15 мая 2019

Используя CTE , статистику по учащемуся можно собрать в отдельный подзапрос:

WITH StatisticsPerStudent AS
(   SELECT studentId,
            COUNT(1) AS TotalSubjectsPassed,
            SUM(mark_obtained) AS TotalMarks
    FROM Performance
    GROUP BY studentId
) SELECT *
    FROM Students
        JOIN StatisticsPerStudent ON StatisticsPerStudent.studentId = Students.studentId
  WHERE 
        StatisticsPerStudent.TotalSubjectsPassed = (SELECT COUNT(1) FROM Subjects) --passed all subjects
        AND StatisticsPerStudent.TotalMarks > 120  --total marks exceeding 120
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...