【问题标题】:SQL Query for Student mark functionality学生标记功能的 SQL 查询
【发布时间】:2012-05-13 07:19:05
【问题描述】:

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

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

请您帮忙解决这些问题。它们看起来很简单,但我无法掌握它。

谢谢!

【问题讨论】:

  • 我无法将其标记为亚马逊面试问题或面试问题,因为它们似乎都是 StackOverflow 中某些清理过程的“请勿使用”标签。 @Mods:如果有任何不一致的问题或标记,请随时编辑。纠正后我将删除此评论。谢谢。

标签: sql database


【解决方案1】:

你只需要把问题分成几个小步骤,然后一个一个地解决

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

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

然后查询谁是SubjectID和max MarkRate的人:

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 关键字实际上更长,您需要为独立查询的列结果 (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

【讨论】:

    【解决方案2】:
    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>
    

    【讨论】:

      【解决方案3】:

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

      架构:

      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,查找最高分的相关详情

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

      结果:

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

      【讨论】:

        【解决方案4】:

        我会说:

        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 解析为名称。
        • 连接到每个科目最高分数的派生表。

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

        【讨论】:

        • 谢谢@Ryan。这个查询非常简洁和漂亮.. 但是,我可以知道您加入主题表以将其解析为他们的名称的第三个条件是否只是在输出中打印主题名称?
        • 还有一个疑问,为什么是“order by”。你添加这个是出于什么动机吗?因为,我得到了相同的输出。
        • 两者都是为了使结果更具可读性。您实际上并没有说是否要将 subid 解析为主题名称。但是由于提供了表格,并且您必须显示 subid 或 subname,我认为它是隐含的。 order by 是为了使输出更好。 (通常是order by。)在任何实际情况下我都会问。
        【解决方案5】:

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

        【讨论】:

          【解决方案6】:

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

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

          要列出在所有科目中得分最高的学生(如果有的话)的姓名(不包括没有分数的科目,因为可以说那时没有最高分),我认为这是可行的,使用 Michael's 中的列名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 的姓名。 (如果学生在一门学科中获得多个分数,则此查询返回学生的姓名,只要其中一个分数是该学科的最高分。)

          【讨论】:

            【解决方案7】:

            使用子查询:

            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;
            

            【讨论】:

              【解决方案8】:
               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
              

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

              【讨论】:

                【解决方案9】:
                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)
                

                【讨论】:

                • 没有任何解释的代码转储很少有帮助。 Stack Overflow 是关于学习的,而不是提供 sn-ps 来盲目复制和粘贴。请edit您的问题并解释它如何比OP提供的更好。
                【解决方案10】:

                我将尝试使用 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
                
                

                【讨论】:

                  【解决方案11】:
                  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;
                  

                  【讨论】:

                  • 请记住,Stack Overflow 不仅仅是为了解决眼前的问题,而是为了帮助未来的读者找到类似问题的解决方案,这需要了解底层代码。这对于我们社区的初学者和不熟悉语法的成员来说尤其重要。鉴于此,您能否edit 您的答案包括对您正在做什么的解释以及为什么您认为这是最好的方法?
                  【解决方案12】:
                  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;
                  

                  【讨论】:

                  • 你好 Kaustubh,欢迎来到 StackOverflow。我看到你的消息是你的第一个答案!你又读了一遍吗?它可读吗?不 !您能否再次编辑它并将您的 SQL 代码格式化为 CODE ?您还可以添加一些 cmets 来解释为什么您的答案比其他人更好吗?
                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2018-10-17
                  • 1970-01-01
                  • 2014-06-14
                  • 2022-06-18
                  • 2015-05-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多