8

我正在做一个 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');

我将不胜感激任何指示或提示,在上线一周后才发现您的工作是错误的,这是非常尴尬的!

4

7 回答 7

4

在 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 小提琴演示

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

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 小提琴演示

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

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注意:我修改了数据以包含平局情况

于 2012-10-24T19:52:21.043 回答
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

于 2012-10-24T19:53:14.367 回答
1

如果两个 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)
                )
        )
        ;
于 2012-10-24T20:03:56.970 回答
1

试试这个解决方案:

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 小提琴演示

于 2012-10-24T19:54:27.890 回答
1

具有相关子查询

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
            )

使用 SQL Fiddle 演示

于 2012-10-24T20:34:18.333 回答
0

只需 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 子句中。

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

于 2012-10-24T19:57:59.097 回答
0

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

  SELECT  test, subject, MIN(score) as minscore
  FROM example_student_scores
  WHERE studentid=94
  GROUP BY test, subject
于 2012-10-24T19:54:33.943 回答