按表达分组

时间:2014-03-31 20:18:56

标签: sql-server-2008

这是我的sqlfiddler bech; http://sqlfiddle.com/#!3/9d859

Grades:

SID CID GRADE
--------------
S1  C1  50
S1  C2  85
S1  C3  60
S1  C4  90
S1  C5  50
S2  C1  30
S2  C2  40
S3  C2  85
S4  C2  80
S4  C4  75
S4  C5  60

我们有成绩表, SID = Student_ID CID = Course_ID

我们希望从“C1”课程中获得最高分的学生的SID。

这是我的解决方案

SELECT DISTINCT SID
FROM GRADES
WHERE GRADE =
    (SELECT max(GRADE)
     FROM GRADES
     GROUP BY CID HAVING CID = 'C1')

在我看来这是错误的,我该如何解决?

2 个答案:

答案 0 :(得分:1)

您必须过滤相应的课程日期,按年级desc排序并获得第一行。

SELECT
  SID
FROM grades
WHERE CID = 'C1'
ORDER BY GRADE DESC
LIMIT 1;

或使用此:

SELECT
  SID
FROM grades G1
WHERE CID = 'C1'
 AND GRADE = (SELECT MAX(GRADE) FROM grades G2 WHERE G2.CID = G1.CID)

更新

上述查询也适用于SQL Server。

但您也可以使用它:

SELECT TOP 1
  SID
FROM grades
WHERE CID = 'C1'
ORDER BY GRADE DESC

如果有多个学生在课程中获得最高分,并且您想要所有这些,则可以使用:

SELECT TOP 1 WITH TIES
  SID
FROM grades
WHERE CID = 'C1'
ORDER BY GRADE DESC

FOR Marcus Adams

经过测试:

Microsoft SQL Server 2014 (CTP2) - 12.0.1524.0 (X64) 
    Oct  3 2013 19:00:26 
    Copyright (c) Microsoft Corporation
    Enterprise Evaluation Edition (64-bit) on Windows NT 6.2  (Build 9200: )
--SET SHOWPLAN_TEXT ON
SELECT
  SID
FROM grades G1
WHERE CID = 'C1'
 AND GRADE = (SELECT MAX(GRADE) FROM grades G2 WHERE G2.CID = G1.CID)
(1 row(s) affected)

StmtText
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
  |--Nested Loops(Inner Join, WHERE:([Expr1002]=[Test].[dbo].[GRADES].[GRADE] as [G1].[GRADE]))
       |--Stream Aggregate(DEFINE:([Expr1002]=MAX([Test].[dbo].[GRADES].[GRADE] as [G2].[GRADE])))
       |    |--Clustered Index Scan(OBJECT:([Test].[dbo].[GRADES].[PK__GRADES__4606D4B55925FD88] AS [G2]), WHERE:([Test].[dbo].[GRADES].[CID] as [G2].[CID]='C1'))
       |--Clustered Index Scan(OBJECT:([Test].[dbo].[GRADES].[PK__GRADES__4606D4B55925FD88] AS [G1]), WHERE:([Test].[dbo].[GRADES].[CID] as [G1].[CID]='C1'))

(4 row(s) affected)
--SET SHOWPLAN_TEXT ON
SELECT
  SID
FROM grades G1
WHERE CID = 'C1'
 AND GRADE = (SELECT MAX(GRADE) FROM grades G2 WHERE G2.CID = 'C1')
(1 row(s) affected)

StmtText
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
  |--Nested Loops(Inner Join, WHERE:([Expr1002]=[Test].[dbo].[GRADES].[GRADE] as [G1].[GRADE]))
       |--Stream Aggregate(DEFINE:([Expr1002]=MAX([Test].[dbo].[GRADES].[GRADE] as [G2].[GRADE])))
       |    |--Clustered Index Scan(OBJECT:([Test].[dbo].[GRADES].[PK__GRADES__4606D4B55925FD88] AS [G2]), WHERE:([Test].[dbo].[GRADES].[CID] as [G2].[CID]='C1'))
       |--Clustered Index Scan(OBJECT:([Test].[dbo].[GRADES].[PK__GRADES__4606D4B55925FD88] AS [G1]), WHERE:([Test].[dbo].[GRADES].[CID] as [G1].[CID]='C1'))

(4 row(s) affected)

答案 1 :(得分:1)

这是错误的,因为您选择的所有学生都与所选课程中的最高成绩相同。 你只想要那些真正完成那门课程的学生。为此,您还需要将课程选择添加到主查询中,而不仅仅是子查询。

你可以这样写:

SELECT DISTINCT
  g.SID
FROM
  GRADES g
WHERE
  g.CID = 'C5' AND
  g.GRADE = (SELECT max(g1.GRADE)
     FROM GRADES g1
     WHERE g1.CID = g.CID)

这将返回所有成绩最高的学生。如果他们获得相同的成绩,这可以是多个学生,但如果您有这样的查询,这似乎是合乎逻辑的。如果你不想那样,你可以使用Hamlet Hakobyan的答案,这将给你一个分享最高分的学生。

注意,我添加DISTINCT只是因为您的示例中有双重数据。目前,学生S1的成绩是同等最高成绩的两倍,这就是为什么如果你不添加DISTINCT它会出现两次。

http://sqlfiddle.com/#!3/9d859/35