SQL查询以对分组数据应用多个条件

时间:2009-02-03 17:29:22

标签: sql

鉴于表格:

Exam (ExamId, SubjectId)
Result (ExamId, StudentId, GradeId)

检索在SubjectId ='数学'中获得GradeId ='A'并且在SubjectId ='English'中获得GradeId ='A'的学生列表的最佳方法是什么,以获取每个科目的最近一次考试?我们可以假设ExamIds随着时间的推移而增加,或者将ExamDate列添加到Exam。

谢谢!

2 个答案:

答案 0 :(得分:3)

您需要做的第一件事是处理“最近的考试”条件。

以下内容按主题获取每位学生的最新考试...

SELECT
  [Result].StudentID,
  [Exam].SubjectID,
  MAX([Exam].id) AS ExamID
FROM
  Result
     INNER JOIN
  Exam
     ON [Exam].id = [Result].ExamID
GROUP BY
  [Result].StudentID,
  [Exam].SubjectID


然后,您需要获得每个考试的成绩,并应用您的限制......

SELECT
  [Recent].StudentID
FROM
(
  SELECT
    [Result].StudentID,
    [Exam].SubjectID,
    MAX([Exam].id) AS ExamID
  FROM
    Result
       INNER JOIN
    Exam
       ON [Exam].id = [Result].ExamID
  GROUP BY
    [Result].StudentID,
    [Exam].SubjectID
)
  AS [Recent]
INNER JOIN
  Result
    ON  [Result].StudentID = [Recent].StudentID
    AND [Result].ExamID    = [Recent].ExamID
GROUP BY
  [Recent].StudentID
HAVING
  MIN(      
    CASE [Exam].SubjectID
       WHEN 'Maths'   THEN CASE WHEN GradeID = 'A' THEN 1 ELSE 0 END
       WHEN 'English' THEN CASE WHEN GradeID = 'B' THEN 1 ELSE 0 END
       ELSE 1
    END
  )
  = 1

如果您知道您只想要MATHS和ENGLISH,可以通过在[Recent]的子查询中添加WHERE子句来加速...

  WHERE
    [Exam].Subject IN ('Maths', 'English')

编辑:

查询的“MIN(CASE)= 1”部分如下工作...
- 如果主题是数学,并且他们得到A,那么1.否则为0.
- 如果主题是英语,并且他们得到B,那么1.否则为0.
- 如果主题是其他任何内容,那么1.

如果其中任何一项返回0,请忽略该学生。

因此,如果学生有数学:A,英语:B,地理:A,地理不会导致0,所以不会导致学生被忽略,无论他们的地理等级如何。

现在考虑更多,如果他们没有数学和/或英语成绩,他们仍然可以通过这项检查。如果您想排除未参加数学和/或英语测试的学生,请改用此HAVING子句......

  SUM(      
    CASE [Exam].SubjectID
       WHEN 'Maths'   THEN CASE WHEN GradeID = 'A' THEN 1 ELSE 0 END
       WHEN 'English' THEN CASE WHEN GradeID = 'B' THEN 1 ELSE 0 END
       ELSE 0
    END
  )
  = 2

ELSE 0确保忽略其他主题,SUM()= 2确保两个条件匹配。

编辑:

将要求放在表格中(并加速一切)......

DECLARE @requirements TABLE (
  SubjectID    NVARCHAR(32),
  GradeID      NCHAR(1)
  )

INSERT INTO @requirements VALUES (N'Maths',   N'A')
INSERT INTO @requirements VALUES (N'English', N'B')

SELECT
  [Recent].StudentID
FROM
(
  SELECT
    [Result].StudentID     AS [StudentID],
    [Exam].SubjectID       AS [SubjectID],
    MAX([Exam].id)         AS [ExamID],
    [Requirements].GradeID AS [RequiredGrade]
  FROM
    Exam
  INNER JOIN
    @requirements [Requirements]
      ON [Requirements].SubjectID = [Exam].SubjectID
  INNER JOIN   
    Result
      ON [Exam].id = [Result].ExamID
  GROUP BY
    [Result].StudentID,
    [Exam].SubjectID,
    [Requirements].GradeID AS RequiredGrade
)
  AS [StudentExam]
INNER JOIN
  Result
    ON  [Result].StudentID = [StudentExam].StudentID
    AND [Result].ExamID    = [StudentExam].ExamID
    AND [Result].GradeID   = [StudentExam].RequiredGrade
GROUP BY
  [Recent].StudentID
HAVING
  COUNT(*) = (SELECT COUNT(*) FROM @requirements)

正如另一篇文章所述,如果你可以在那里获得ExamDate,那将比ExamID列更可靠。还应该说,只要您对数据库有足够的控制权,就应该能够阻止身份值做除了前进之外的任何事情。

答案 1 :(得分:0)

如果你从这篇文章中得不到任何其他内容,那就让它成为:不要以为ID会一直增加。这只会导致黑暗面。投入考试日期(我不知道为什么你不会有一个,除非你的教授忘了把它放在作业问题中)并用它来确定考试是在考试之前还是之后。

那就是说,这里有两种可能的方法。两者都没有经过测试,所以你应该测试它们,并确保你完全理解这些解决方案。毕竟他们可能正在参加考试。 :)

SELECT
     ENG.StudentID
FROM
     Results ENG_RES
INNER JOIN Exams ENG_EX ON
     ENG_EX.ExamID = ENG_RES.ExamID AND
     ENG_EX.SubjectID = 'English'
INNER JOIN Results MATH_RES ON
     MATH_RES.StudentID = ENG_RES.StudentID AND
     MATH_RES.GradeID = 'A'
INNER JOIN Exams MATH_EX
     MATH_EX.ExamID = MATH_RES.ExamID AND
     MATH_EX.SubjectID = 'Math'
LEFT OUTER JOIN Results MATH_RES2 ON
     MATH_RES2.StudentID = ENG_RES.StudentID
LEFT OUTER JOIN Exams MATH_EX2
     MATH_EX2.ExamID = MATH_RES2.ExamID AND
     MATH_EX2.SubjectID = 'Math' AND
     MATH_EX2.ExamDate > MATH_EX.ExamDate
LEFT OUTER JOIN Results ENG_RES2 ON
     ENG_RES2.StudentID = ENG_RES.StudentID
LEFT OUTER JOIN Exams ENG_EX2
     ENG_EX2.ExamID = ENG_RES2.ExamID AND
     ENG_EX2.SubjectID = 'English' AND
     ENG_EX2.ExamDate > ENG_EX.ExamDate
WHERE
     ENG_RES.GradeID = 'B' AND
     MATH_EX2.ExamID IS NULL AND
     ENG_EX2.ExamID IS NULL

或者:

SELECT
     ENG.StudentID
FROM
     Results ENG_RES
INNER JOIN Exams ENG_EX ON
     ENG_EX.ExamID = ENG_RES.ExamID AND
     ENG_EX.SubjectID = 'English'
INNER JOIN Results MATH_RES ON
     MATH_RES.StudentID = ENG_RES.StudentID AND
     MATH_RES.GradeID = 'A'
INNER JOIN Exams MATH_EX
     MATH_EX.ExamID = MATH_RES.ExamID AND
     MATH_EX.SubjectID = 'Math'
WHERE
     ENG_RES.GradeID = 'B' AND
     NOT EXISTS
          (
               SELECT *
               FROM
                    Results SQR1
               INNER JOIN Exams SQE1 ON
                    SQE1.ExamID = SQR1.ExamID AND
                    SQE1.SubjectID = 'Math' AND
                    SQE1.ExamDate > MATH_EX.ExamDate
               WHERE
                    SQR1.StudentID = ENG_RES.StudentID
          ) AND
     NOT EXISTS
          (
               SELECT *
               FROM
                    Results SQR2
               INNER JOIN Exams SQE2 ON
                    SQE2.ExamID = SQR2.ExamID AND
                    SQE2.SubjectID = 'English' AND
                    SQE2.ExamDate > ENG_EX.ExamDate
               WHERE
                    SQR2.StudentID = ENG_RES.StudentID
          )