【问题标题】:Selecting the minimum of two distinct values选择两个不同值中的最小值
【发布时间】:2012-10-14 22:11:15
【问题描述】:

我正在做一个 sql 选择,我需要获取一个 ID 的所有最小记录。我的问题是,虽然我认为我的代码可以正常工作,但它通常会为其他列返回不正确的值,这显然是因为我未能使用必须支持我的聚合 min 函数。这是我的代码。

SELECT *
FROM example_students
INNER JOIN
(SELECT  id, student, test, subject, MIN(score), semester
FROM example_student_scores
WHERE studentid=94
GROUP BY studentid, test, subject) as scores
ON example_students.id=scores.studentid

这就是我希望我的代码做的事情。

从示例表中选择学生 ID#94 的每个不同测试和科目组合的最低分数

这里有两个(经过大量修改的)示例表(我在这里和我的代码中都更改了所有列名。

example_students
id    name
----+-----------+
94    Bob
1023  Thomas

.

example_students_scores
id    studentId     test       subject     score       semester
----+-----------+-----------+-----------+-----------+-----------

0    94          quiz        math        46          fall
1    94          quiz        math        71          fall
2    94          quiz        math        63          winter
3    94          midterm     math        94          winter
4    94          midterm     science     72          fall
5    94          quiz        math        50          spring
6    94          final       math        76          spring
7    1023        quiz        math        6           spring
8    1023        quiz        math        52          winter
9    1023        quiz        science     68          fall
..*

结果应该是

results
id    studentId     test       subject     score       semester
----+-----------+-----------+-----------+-----------+-----------
0    94          quiz        math        46          fall
3    94          midterm     math        94          winter
4    94          midterm     science     72          fall
6    94          final       math        76          spring

问题是,我会得到错误的学期列(以及我在现场工作中拥有的所有其他列)的值。

考虑到我花了很长时间才搞定,这里是创建两个示例数据库的 sql:

drop table if exists example_students;
drop table if exists example_students_scores;
create table example_students(
    id int(10) primary key,
    name char(25)
);
create table example_students_scores(
    id int(10) not null,
    studentId int(10) not null,
    test char(20),
    subject char(20),
    score int(10) not null default '0',
    semester char(20),
    primary key (id),
    index studentid (studentid)
);
insert into example_students values ('94','Bob');
insert into example_students values ('1023','Thomas');
insert into example_students_scores values ('0'    ,'94'          ,'quiz'        ,'math'        ,'46'          ,'fall');
insert into example_students_scores values ('1'    ,'94'          ,'quiz'        ,'math'        ,'71'          ,'fall');
insert into example_students_scores values ('2'    ,'94'          ,'quiz'        ,'math'        ,'63'          ,'winter');
insert into example_students_scores values ('3'    ,'94'          ,'midterm'     ,'math'        ,'94'          ,'winter');
insert into example_students_scores values ('4'    ,'94'          ,'midterm'     ,'science'     ,'72'          ,'fall');
insert into example_students_scores values ('5'    ,'94'          ,'quiz'        ,'math'        ,'50'          ,'spring');
insert into example_students_scores values ('6'    ,'94'          ,'final'       ,'math'        ,'76'          ,'spring');
insert into example_students_scores values ('7'    ,'1023'        ,'quiz'        ,'math'        ,'6'           ,'spring');

如果您有任何指点或提示,我将不胜感激,在上线一周后才发现您的工作有问题,这非常尴尬!

【问题讨论】:

  • I need to get all the smallest records for an ID. 请定义最小记录
  • 6小于46,小于56。0是最小的。
  • 6 不是记录,46 不是记录,56 不是记录。它们可能是记录中的字段,但您要比较哪些字段
  • 请将 student_results 的第二行更改为 1 94 quiz math 46 fall 以了解重复项的含义。

标签: mysql sql aggregate-functions


【解决方案1】:

在 MySQL 中,当您在 select 中而不是在 group by 中包含一个字段时,您会得到一个任意值。在这种情况下,您不能简单地将学期和考试 ID 包含在 group by 中,因为您不会得到您想要的结果。

为了做到这一点,您必须找到每个学生、测试、科目的最低分数,然后将其重新加入原始表格

SELECT * 
FROM   example_students_scores ess 
       INNER JOIN (SELECT studentid, 
                          test, 
                          subject, 
                          Min(score) score 
                   FROM   example_students_scores 
                   WHERE  studentid = 94 
                   GROUP  BY studentid, 
                             test, 
                             subject) scores 
               ON ess.studentid = scores.studentid 
                  AND ess.test = scores.test 
                  AND ess.subject = scores.subject 
                  AND ess.score = scores.score 

SQL Fiddle Demo

另一种不太传统的方法是对不等式进行自反连接。

SELECT 
    s.*
FROM
    example_students_scores s
    LEFT JOIN example_students_scores s2
    ON s.studentID = s2.studentID
      AND s.test = s2.test
      AND s.subject = s2.subject
      AND s.score > s2.score
WHERE 
     s.studentid = 94 
     AND 
     s2.score is null

SQL Fiddle Demo

如果您有兴趣创建一个决胜局,只需添加一个或条件

SELECT 
    s.*
FROM
    example_students_scores s
    LEFT JOIN example_students_scores s2
    ON s.studentID = s2.studentID
      AND s.test = s2.test
      AND s.subject = s2.subject
      AND (s.score > s2.score
           or s.id > s2.id ) -- Added for tie breaker

WHERE 
     s.studentid = 94 
     AND 
     s2.score is null

SQL Fiddle Demo注意:我修改了数据以包含平局情况

【讨论】:

  • 非常感谢,我不知道你可以对多个表达式执行连接!
【解决方案2】:

这应该适合你:

select ss2.id score_id,
  ss2.studentid,
  ss1.test,
  ss2.subject,
  ss1.score,
  ss2.semester
from example_students st
left join
(
  select min(score) score, test, subject, studentid
  from example_students_scores
  group by test, studentid, subject
) ss1
  on st.id = ss1.studentid
left join example_students_scores ss2
  on st.id = ss2.studentid
  and ss1.score = ss2.score
  and ss1.test = ss2.test
where st.id = 94
order by ss2.id

SQL Fiddle with Demo

【讨论】:

    【解决方案3】:

    试试这个解决方案:

    select es.name, e.studentid, e.test, e.subject, e.score as MinScore, e.semester 
    from  example_students_scores e
    join (
           select studentid, test, subject, min(score) as score
           from example_students_scores
            group by studentid, test, subject) e2 on  e.studentid=e2.studentid
                                                  and e.test=e2.test 
                                                  and e.subject=e2.subject
                                                  and e.score=e2.score
    join example_students es on e.studentid = es.id
    

    SQL Fiddle演示

    【讨论】:

      【解决方案4】:

      以下查询是否符合您的要求?

        SELECT  test, subject, MIN(score) as minscore
        FROM example_student_scores
        WHERE studentid=94
        GROUP BY test, subject
      

      【讨论】:

        【解决方案5】:

        只需 SELECT 和 GROUP BY 您需要的列:

        SELECT *
        FROM example_students
        INNER JOIN
        (SELECT studentid, test, subject, MIN(score), semester
        FROM example_student_scores
        GROUP BY studentid, test, subject,semester) scores
        ON example_students.id=scores.studentid
        WHERE studentid=94
        

        当您对列使用聚合函数时,SELECT 子句中的所有其他属性必须出现在 GROUP BY 子句中。

        查询的其余部分对我来说听起来不错。

        【讨论】:

        • 您的结果中缺少 testID。它还会产生 8 条记录而不是 4 条
        【解决方案6】:

        如果两个 example_student_scores 恰好具有相同的分数,您需要避免输出中出现重复。 Min() 会导致重复。

        执行该任务的一个极好的方法是使用窗口函数和 row_number()。

        如果窗口函数不可用,模拟“第一组”功能的以下方法可能是:

        SELECT * 
        FROM example_students es
        JOIN example_student_scores ss ON ss.studentid= es.id
        WHERE es.id = 94
        AND NOT EXISTS (
                SELECT *
                FROM example_student_scores nx
                WHERE nx.studentid = ss.studentid
                AND nx.test = ss.test
                AND nx.subject = ss.subject
                AND ( nx.score < ss.score
                        -- tie breaker
                        OR ( nx.score == ss.score AND nx.id < ss.id)
                        )
                )
                ;
        

        【讨论】:

        • 非常好。我试图找出一种在没有分区 row_number 或其他子查询的情况下进行决胜局的好方法,您的回答启发了我的最后一个查询。请注意,在 MySQL 中模拟行号很容易,但我还没有弄清楚如何进行分区。顺便说一句,您有几个语法错误。 (== 和三个部分名称nx.scores.studentidss.scores.studentid
        • 对不起 3 级名称。它们是由复制粘贴您的片段并在最后一刻添加相关名称引起的。
        【解决方案7】:

        带有相关子查询

        select 
           * 
        from 
           example_students_scores s1 
        where 
           studentId = 94 and 
           score  = (select 
                         min(score)
                     from 
                         example_students_scores s2 
                     where
                         s2.studentId = s1.studentId and 
                         s2.subject = s1.subject and 
                         s2.test = s1.test 
                     group by 
                         studentId, test, subject
                    )
        

        Demo with SQL Fiddle

        【讨论】:

          猜你喜欢
          • 2016-09-08
          • 2011-12-14
          • 1970-01-01
          • 2013-10-27
          • 2011-10-04
          • 1970-01-01
          • 2017-08-20
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多