用于学生标记功能的SQL查询

时间:2012-05-13 07:19:05

标签: sql database

这是亚马逊的一个面试问题,用来测试基本的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”是错误的。

我觉得问题的另一个变体是,如果有一个以上的学生在一个科目中具有最高分,那么即使他们的名字也应该被包括在内。

请帮助解决这些问题。它们似乎很简单,但我无法掌握它。

谢谢!

11 个答案:

答案 0 :(得分:4)

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

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

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;

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

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元组测试仍然是任何其他名称的连接:

转换此..

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关键字实际上更长,您需要为独立查询的列结果(max(MarkRate) AS MarkRate)以及子查询本身(as x)添加别名。无论如何,这只是风格问题,我更喜欢IN子句,因为意图更清晰。仅使用JOIN来反映数据关系。

无论如何,这里的查询适用于所有不支持元组测试的数据库(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

答案 1 :(得分: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 Fiddle Example

  1. 查找每个主题的最大分数
  2. 加入MarksStudentSubject以查找该最高分的相关详细信息
  3. 这也照顾了最高分的重复学生

    结果:

    STUDENTNAME SUBJECTNAME     MARK
    Alfred  English     87
    Betty   Maths           82
    Chris   Maths           82
    Chris   Science     83
    

答案 2 :(得分: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解析为名称。
  • 连接到每个主题中最大标记的派生表。

只有获得最高分的学生才能满足所有三种参赛条件。这列出了所有获得该最高分的学生,因此如果有关系,则两者都列出。

答案 3 :(得分:0)

我喜欢使用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

答案 4 :(得分:0)

只是为了好玩,考虑一下对OP描述的非常直译的不同问题:“编写一个查询来打印在 中获得最大分数的学生的名单列表 主题。“

如果学生在任何 一个 科目中获得最高分,但是不一定在 所有 科目。由于OP提出的问题并未要求输出中的主题名称,这是一种似是而非的解释。

列出在所有科目中获得最高分的学生(如果有)的名字(不包括没有分数的科目,因为当时没有最大分数),我认为这是有效的,使用迈克尔的专栏名称SQL Fiddle,我改编了here

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的名称。 (如果学生在某个科目中收到多个分数,则此查询会返回学生的姓名,只要其中一个分数是该科目的最高分。)

答案 5 :(得分:0)

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>

答案 6 :(得分: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;

将Rank与分区结合使用:

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;

答案 7 :(得分: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

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

答案 8 :(得分: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)

答案 9 :(得分: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);

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

学生姓名| subject_name |标记

计划执行步骤

  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

答案 10 :(得分:-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;