【问题标题】:Joining tables based on the maximum value根据最大值连接表
【发布时间】:2010-12-24 05:33:18
【问题描述】:

这是我所说的一个简化示例:

Table: students      exam_results
_____________       ____________________________________
| id | name |       | id | student_id | score |   date |
|----+------|       |----+------------+-------+--------|
|  1 | Jim  |       |  1 |          1 |    73 | 8/1/09 | 
|  2 | Joe  |       |  2 |          1 |    67 | 9/2/09 |
|  3 | Jay  |       |  3 |          1 |    93 | 1/3/09 |
|____|______|       |  4 |          2 |    27 | 4/9/09 |
                    |  5 |          2 |    17 | 8/9/09 |
                    |  6 |          3 |   100 | 1/6/09 |
                    |____|____________|_______|________|

为了这个问题,假设每个学生至少记录了一个考试结果。

您将如何选择每个学生以及他们的最高分? 编辑:...以及该记录中的其他字段?

预期输出:

_________________________
| name | score |   date |
|------+-------|--------|
|  Jim |    93 | 1/3/09 |
|  Joe |    27 | 4/9/09 |
|  Jay |   100 | 1/6/09 |
|______|_______|________|

欢迎使用所有类型的 DBMS 回答。

【问题讨论】:

  • 你将如何解决关系?在您的示例中,Jim 两次得分 93 应该选择哪条记录?
  • 在我自己的表格中,(与学生和考试无关)这不会发生。哪一个都可以?

标签: sql mysql sql-server oracle join


【解决方案1】:

回答 EDITED 问题(即也获取相关列)。

在 Sql Server 2005+ 中,最好的方法是将ranking/window functionCTE 结合使用,如下所示:

with exam_data as
(
    select  r.student_id, r.score, r.date,
            row_number() over(partition by r.student_id order by r.score desc) as rn
    from    exam_results r
)
select  s.name, d.score, d.date, d.student_id
from    students s
join    exam_data d
on      s.id = d.student_id
where   d.rn = 1;

对于符合 ANSI-SQL 的解决方案,子查询和自联接将起作用,如下所示:

select  s.name, r.student_id, r.score, r.date
from    (
            select  r.student_id, max(r.score) as max_score
            from    exam_results r
            group by r.student_id
        ) d
join    exam_results r
on      r.student_id = d.student_id
and     r.score = d.max_score
join    students s
on      s.id = r.student_id;

最后一个假设没有重复的 student_id/max_score 组合,如果有和/或您想计划对它们进行重复数据删除,则需要使用另一个子查询来加入确定性来决定哪个记录拉。例如,假设您不能有同一日期的给定学生的多条记录,如果您想根据最近的 max_score 打破平局,您可以执行以下操作:

select  s.name, r3.student_id, r3.score, r3.date, r3.other_column_a, ...
from    (
            select  r2.student_id, r2.score as max_score, max(r2.date) as max_score_max_date
            from    (
                        select  r1.student_id, max(r1.score) as max_score
                        from    exam_results r1
                        group by r1.student_id
                    ) d
            join    exam_results r2
            on      r2.student_id = d.student_id
            and     r2.score = d.max_score
            group by r2.student_id, r2.score
        ) r
join    exam_results r3
on      r3.student_id = r.student_id
and     r3.score = r.max_score
and     r3.date = r.max_score_max_date
join    students s
on      s.id = r3.student_id;

编辑:由于 Mark 在 cmets 中的出色表现,添加了适当的重复数据删除查询

【讨论】:

  • 如果日期不同,我认为不同的方法无法消除重复的联系。
  • 好点马克 - 需要在另一个子查询中使用确定性的东西才能在 ANSI 查询中正确地重复数据删除。我会编辑以反映...
  • Invalid column name 'score':我认为你有几个表名混淆了。
  • 是的,这就是您在文本编辑器中编写查询时得到的结果...我刚刚对其进行了调整,在最终连接条件上向后过滤了过滤器
【解决方案2】:
SELECT s.name,
    COALESCE(MAX(er.score), 0) AS high_score
FROM STUDENTS s
    LEFT JOIN EXAM_RESULTS er ON er.student_id = s.id
GROUP BY s.name

【讨论】:

  • 这假设可能有学生没有相关的考试。
  • 啊,好吧,看来我问的问题很糟糕。我会改写的。
  • 必须添加一个 group by 子句才能使其工作,即“group by s.name”
【解决方案3】:

试试这个,

Select student.name, max(result.score) As Score from Student
        INNER JOIN
    result
        ON student.ID = result.student_id
GROUP BY
    student.name

【讨论】:

  • 感谢 Zinx,但是我把问题的原始措辞填满了。实际上,我需要知道的不仅仅是高分:我还需要知道记录中保持高分的所有其他字段。
  • Zinx,高亮你的代码并按Ctrl+K格式化查询语法,更容易阅读
【解决方案4】:

借助 Oracle 的分析功能,这很容易:

SELECT DISTINCT
       students.name
      ,FIRST_VALUE(exam_results.score)
       OVER (PARTITION BY students.id
             ORDER BY exam_results.score DESC) AS score
      ,FIRST_VALUE(exam_results.date)
       OVER (PARTITION BY students.id
             ORDER BY exam_results.score DESC) AS date
FROM   students, exam_results
WHERE  students.id = exam_results.student_id;

【讨论】:

  • 我知道 PostgreSQL 不在问题列表中,但如果有人偶然发现这个问题,它也有 window functions 这样做。
【解决方案5】:
Select Name, T.Score, er. date 
from Students S inner join
          (Select Student_ID,Max(Score) as Score from Exam_Results
           Group by Student_ID) T 
On S.id=T.Student_ID inner join Exam_Result er
On er.Student_ID = T.Student_ID And er.Score=T.Score

【讨论】:

    【解决方案6】:

    使用 MS SQL Server:

    SELECT name, score, date FROM exam_results
    JOIN students ON student_id = students.id
    JOIN (SELECT DISTINCT student_id FROM exam_results) T1
    ON exam_results.student_id = T1.student_id
    WHERE exam_results.id = (
        SELECT TOP(1) id FROM exam_results T2
        WHERE exam_results.student_id = T2.student_id
        ORDER BY score DESC, date ASC)
    

    如果得分相同,则返回最早的日期(将 date ASC 更改为 date DESC 以返回最新的日期)。

    输出:

    Jim 93  2009-01-03 00:00:00.000
    Joe 27  2009-04-09 00:00:00.000
    Jay 100 2009-01-06 00:00:00.000
    

    试验台:

    CREATE TABLE students(id int , name nvarchar(20) );
    
    CREATE TABLE exam_results(id int , student_id int , score int, date datetime);
    
    INSERT INTO students
    VALUES
    (1,'Jim'),(2,'Joe'),(3,'Jay')
    
    INSERT INTO exam_results VALUES
    (1, 1, 73, '8/1/09'), 
    (2, 1, 93, '9/2/09'),
    (3, 1, 93, '1/3/09'),
    (4, 2, 27, '4/9/09'),
    (5, 2, 17, '8/9/09'),
    (6, 3, 100, '1/6/09')
    
    SELECT name, score, date FROM exam_results
    JOIN students ON student_id = students.id
    JOIN (SELECT DISTINCT student_id FROM exam_results) T1
    ON exam_results.student_id = T1.student_id
    WHERE exam_results.id = (
        SELECT TOP(1) id FROM exam_results T2
        WHERE exam_results.student_id = T2.student_id
        ORDER BY score DESC, date ASC)
    

    在 MySQL 上,我认为您可以在语句末尾将 TOP(1) 更改为 LIMIT 1。不过我还没有测试过。

    【讨论】:

      猜你喜欢
      • 2023-03-19
      • 2015-06-06
      • 1970-01-01
      • 1970-01-01
      • 2013-07-25
      • 1970-01-01
      • 1970-01-01
      • 2021-01-14
      • 2013-04-02
      相关资源
      最近更新 更多