5

把这个作为亚马逊的一个面试问题来测试基本的 SQL 技能,我有点失败了。考虑下表:

Student - Stid, Stname, Details
Subject - Subid, Subname
Marks - Stid, Subid, mark

编写查询以打印在每个科目中得分最高的学生的姓名列表。

我给出的错误答案是:

select A.Stname from A as Student, B as 
(select Stid, Subid, max(mark) from Marks groupby Subid) where A.Stid = B.Stid

我在想你可以有一个表 B,你可以在其中单独获得最高分,并将其与学生表 A 中的名称匹配。但事实证明我的“groupby”是错误的。

我认为可以提出的问题的另一个变体是,如果有一门学科的得分最高的学生不止一个,甚至他们的名字也应该包括在内。

你能帮忙解决这些问题吗?它们似乎很简单,但我无法掌握它。

谢谢!

4

12 回答 12

6

您只需将问题划分为一些小步骤并逐个解决

首先,获得每个科目的最高分:

select SubjectID, max(MarkRate)
from Mark
group by SubjectID;

然后查询谁是具有最大 MarkRate 的 SubjectID:

select SubjectID, MarkRate, StudentID
from Mark
where (SubjectID,MarkRate)
in
  (
  select SubjectID, max(MarkRate)
  from Mark
  group by SubjectID
  )
order by SubjectID, StudentID;

然后获取学生的姓名,而不是只显示 StudentID:

select SubjectName, MarkRate, StudentName
from Mark
join Student using(StudentID)
join Subject using(SubjectID)
where (SubjectID,MarkRate)
in
  (
  select SubjectID, max(MarkRate)
  from Mark
  group by SubjectID
  )
order by SubjectName, StudentName

除了数据库厂商在加入和关联结果方面的人为差异外,基本步骤是相同的​​;首先,将问题分成一小部分,然后在解决每个问题时将它们整合起来,这样就不会那么混乱了。


样本数据:

CREATE TABLE Student
    (StudentID int, StudentName varchar(6), Details varchar(1));    
INSERT INTO Student
    (StudentID, StudentName, Details)
VALUES
    (1, 'John', 'X'),
    (2, 'Paul', 'X'),
    (3, 'George', 'X'),
    (4, 'Paul', 'X');

CREATE TABLE Subject
    (SubjectID varchar(1), SubjectName varchar(7));    
INSERT INTO Subject
    (SubjectID, SubjectName)
VALUES
    ('M', 'Math'),
    ('E', 'English'),
    ('H', 'History');

CREATE TABLE Mark
    (StudentID int, SubjectID varchar(1), MarkRate int);    
INSERT INTO Mark
    (StudentID, SubjectID, MarkRate)
VALUES
    (1, 'M', 90),
    (1, 'E', 100),
    (2, 'M', 95),
    (2, 'E', 70),
    (3, 'E', 95),
    (3, 'H', 98),
    (4, 'H', 90),
    (4, 'E', 100);

现场测试:http ://www.sqlfiddle.com/#!1/08728/3


IN tuple test 仍然是任何其他名称的连接:

转换这个..

select SubjectName, MarkRate, StudentName
from Mark
join Student using(StudentID)
join Subject using(SubjectID)

where (SubjectID,MarkRate)
in
  (
  select SubjectID, max(MarkRate)
  from Mark
  group by SubjectID
  )

order by SubjectName, StudentName

..加入:

select SubjectName, MarkRate, StudentName
from Mark
join Student using(StudentID)
join Subject using(SubjectID)

join
  (
  select SubjectID, max(MarkRate) as MarkRate
  from Mark
  group by SubjectID
  ) as x using(SubjectID,MarkRate)

order by SubjectName, StudentName

将此代码与直接它的代码进行对比。看看独立查询上的 JOIN 看起来如何像一个 IN 构造?它们看起来几乎一样,只是将 IN 替换为 JOIN 关键字;并且用 JOIN 替换的 IN 关键字实际上更长,您需要为独立查询的列 result( max(MarkRate) AS MarkRate) 以及子查询本身 ( as x) 取别名。无论如何,这只是风格问题,我更喜欢IN子句,因为意图更清晰。使用 JOIN 只是为了反映数据关系。

无论如何,这是适用于所有不支持元组 test( IN) 的数据库的查询:

select sb.SubjectName, m.MarkRate, st.StudentName
from Mark as m
join Student as st on st.StudentID = m.StudentID
join Subject as sb on sb.SubjectID = m.SubjectID

join
  (
  select SubjectID, max(MarkRate) as MaxMarkRate
  from Mark
  group by SubjectID
  ) as x on m.SubjectID = x.SubjectID AND m.MarkRate = x.MaxMarkRate

order by sb.SubjectName, st.StudentName

现场测试:http ://www.sqlfiddle.com/#!1/08728/4

于 2012-05-13T07:52:55.160 回答
3
SQL> select * from stud;

 STUDENTID NAME                           DETAILS
---------- ------------------------------ ------------------------------
         1 Alfred                         AA
         2 Betty                          BB
         3 Chris                          CC

SQL> select * from subject;

 SUBJECTID NAME
---------- ------------------------------
         1 Maths
         2 Science
         3 English

SQL> select * from marks;

 STUDENTID  SUBJECTID       MARK
---------- ---------- ----------
         1          1         61
         1          2         75
         1          3         87
         2          1         82
         2          2         64
         2          3         77
         3          1         82
         3          2         83
         3          3         67

9 rows selected.

SQL> select name, subjectid, mark
  2  from (select name, subjectid, mark, dense_rank() over(partition by subjectid order by mark desc) rank
  3  from stud st, marks mk
  4  where st.studentid=mk.studentid)
  5  where rank=1;

NAME                            SUBJECTID       MARK
------------------------------ ---------- ----------
Betty                                   1         82
Chris                                   1         82
Chris                                   2         83
Alfred                                  3         87

SQL>
于 2015-09-23T18:15:45.030 回答
2

我的尝试 - 我将从最大标记开始并从那里构建

架构:

CREATE TABLE Student (
  StudentId int,
  Name nvarchar(30),
  Details nvarchar(30)
)
CREATE TABLE Subject (
  SubjectId int,
  Name nvarchar(30)
)
CREATE TABLE Marks (
  StudentId int,
  SubjectId int,
  Mark int
)

数据:

INSERT INTO Student (StudentId, Name, Details) 
VALUES (1,'Alfred','AA'), (2,'Betty','BB'), (3,'Chris','CC')

INSERT INTO Subject (SubjectId, Name)
VALUES (1,'Maths'), (2, 'Science'), (3, 'English')

INSERT INTO Marks (StudentId, SubjectId, Mark)
VALUES 
(1,1,61),(1,2,75),(1,3,87),
(2,1,82),(2,2,64),(2,3,77),
(3,1,82),(3,2,83),(3,3,67)
GO

我的查询是:

;WITH MaxMarks AS (
  SELECT SubjectId, MAX(Mark) as MaxMark
    FROM Marks
    GROUP BY SubjectId
 )
SELECT s.Name as [StudentName], sub.Name AS [SubjectName],m.Mark
FROM MaxMarks mm
INNER JOIN Marks m 
ON m.SubjectId = mm.SubjectId 
AND m.Mark = mm.MaxMark
INNER JOIN Student s
ON s.StudentId = m.StudentId
INNER JOIN Subject sub
ON sub.SubjectId = mm.SubjectId

SQL 小提琴示例

  1. 找出每个科目的最高分
  2. 加入MarksStudentSubject找到最高分的相关详情

这也照顾了最高分的重复学生

结果:

STUDENTNAME SUBJECTNAME     MARK
Alfred  English     87
Betty   Maths           82
Chris   Maths           82
Chris   Science     83
于 2012-05-13T07:24:36.677 回答
2

我会说:

select s.stname, s2.subname, highmarks.mark
from students s
join marks m on s.stid = m.stid
join Subject s2 on m.subid = s2.subid
join (select subid, max(mark) as mark
  from marks group by subid) as highmarks
    on highmarks.subid = m.subid and highmarks.mark = m.mark
order by subname, stname;

SQLFiddle在这里: http ://sqlfiddle.com/#!2/5ef84/3

这是一个:

  • 在学生表上选择以获取可能的学生
  • 加入分数表以将学生与分数相匹配,
  • 连接到主题表以将主题 ID 解析为名称。
  • 连接到每个主题的最高分数的派生表。

只有获得最高分的学生才能满足所有三个加入条件。这列出了所有获得最高分数的学生,所以如果有平局,都会列出。

于 2012-05-13T07:43:45.147 回答
1

我喜欢使用 Windows 函数的简单解决方案:

select t.*
from (select student.*, su.subname, max(mark) over (partition by subid) as maxmark
      from marks m join
           students st
           on m.stid = st.stid join
           subject su
           on m.subid = su.subid
     ) t
where t.mark = maxmark

或者,或者:

select t.*
from (select student.*, su.subname, rank(mark) over (partition by subid order by mark desc) as markseqnum
      from marks m join
           students st
           on m.stid = st.stid join
           subject su
           on m.subid = su.subid
     ) t
where markseqnum = 1
于 2012-05-13T13:47:08.177 回答
0

只是为了好玩,考虑一下对 OP 的描述进行非常字面解释时会遇到的不同问题:“编写查询以打印在每个科目中得分最高的学生的姓名列表。”

那些在这里回答的人已经写了查询,列出一个学生是否在任何门科目中得分最高,但不一定在所有科目中得分。由于 OP 提出的问题不要求在输出中提供主题名称,因此这是一个合理的解释。

要列出在所有科目中得分最高的学生(如果有的话)的姓名(不包括没有分数的科目,因为可以说那时没有最高分),我认为这是可行的,使用 Michael's SQL Fiddle 中的列名,我在这里适应了。

select StudentName
from Student 
where not exists (
  select * from Subject
  where exists (
    select * from Mark as M1
    where M1.SubjectID = Subject.SubjectID
    and M1.StudentID <> Student.StudentID
    and not exists (
      select * from Mark as M2
      where M2.StudentID = Student.StudentID
      and M2.SubjectID = M1.SubjectID
      and M2.MarkRate >= M1.MarkRate
    )
  )
)

换句话说,如果没有科目中某人对该科目的分数不匹配或超过属于 X 的同一科目的某个分数,则选择学生 X 的姓名。(如果学生在一门学科中获得了多个分数,则此查询将返回学生的姓名,只要其中一个分数是该学科的最高分。)

于 2012-05-14T00:18:17.750 回答
0

使用子查询:

select m.subid, m.stid, m.mark
from marks m,
    (select m2.subid, max(m2.mark) max_mark
    from marks m2
    group by subid) subq

where subq.subid = m.subid
and subq.max_mark = m.mark
order by 1,2;

使用带有分区的等级:

select subid, stid, mark
from (select m.subid, m.stid, m.mark,
rank() over (partition by m.subid order by m.mark desc) Mark_Rank
from marks m)
where Mark_Rank = 1
order by 1,2;
于 2016-09-19T20:01:29.237 回答
0
 select max(m.mark)  as maxMarkObtained,su.Subname from Student s 
  inner join Marks m on s.Stid=m.Stid inner join [Subject] su on
  su.Subid=m.Subid group by su.Subname

我已经执行了,这应该可以。

于 2018-03-18T09:25:33.617 回答
0
Select S.StudentName 
From Student S 
where S.StudentID IN 
    (Select StudentID from (
        ( Select Max(MarkRate)as MarkRate,SubjectID From  Mark Group by SubjectID)) MaxMarks, Mark
 where MaxMarks.SubjectID= Mark.SubjectID AND MaxMarks.MarkRate=Mark.MarkRate)
于 2020-05-07T18:25:38.240 回答
0

我将尝试使用 CTE 和窗口函数 rank() 通过一个查询来获得答案

创建表

create table Students 
(student_id int,
Name varchar(255),
details varchar(255));

create table Subject(
Sub_id int,
name varchar(255));

create table marks
(student_id int,
subject_id int,
mark int);

答案应该是包含以下字段的表格

学生姓名 | 主题名 | 标记

计划执行步骤

  1. 创建 CTE
  • 加入表格;
  • 对主题进行排名,按标记降序排列
  1. 从 CTE 中选择输出字段
with CTE as (select s.name, sb.name as subject_name, m.mark, rank() over(partition by sb.name order by m.mark desc) as rn 
from Students s
join marks m on s.student_id = m.student_id
join subject sb
on sb.Sub_id = m.subject_id)
select name , subject_name, mark
from CTE
where rn = 1

于 2020-11-29T07:51:31.950 回答
0
select Student.Name,dt.Mark from Student inner join 
(
select StudentId, Mark from Marks where Mark in 
(
select Max(Mark) from Marks group by SubjectId
)
)dt
on dt.StudentId = Student.StudentId;
于 2021-12-30T21:04:16.350 回答
-1
SELECT          subjectname,
                studentname 
FROM            student s 
INNER JOIN      mark m 
  ON            s.studid = m.studid 
INNER JOIN      subject su 
  ON            su.subjectid = m.subjectid 
INNER JOIN (
  SELECT        subjectid,
                max(value) AS maximum 
  FROM          mark 
  GROUP BY      subjectid
)               highmark h 
  ON            h.subjectid = m.subjectid 
  AND           h.maximum = m.value;
于 2020-06-07T14:07:02.663 回答