按日期

时间:2016-05-10 12:30:07

标签: mysql greatest-n-per-group

我有两个mysql最大n组,最新的问题:

考虑到一个学生表和一个成绩表,我想让所有学生都显示他们最近的成绩。

架构脚本:

    CREATE TABLE student (
      id int(11) NOT NULL AUTO_INCREMENT,
      `name` varchar(255) NOT NULL,
      PRIMARY KEY (id)
    ) ENGINE=InnoDB  DEFAULT CHARSET=latin1;

    INSERT INTO student VALUES(1, 'jim');
    INSERT INTO student VALUES(2, 'mark ');
    INSERT INTO student VALUES(3, 'john');

    CREATE TABLE grades (
      id int(11) NOT NULL AUTO_INCREMENT,
      student_id int(11) NOT NULL,
      grade int(11) NOT NULL,
      `date` date DEFAULT NULL,
      PRIMARY KEY (id)
    ) ENGINE=InnoDB  DEFAULT CHARSET=latin1;


    INSERT INTO grades VALUES(1, 1, 6, NULL);
    INSERT INTO grades VALUES(2, 1, 8, NULL);
    INSERT INTO grades VALUES(3, 1, 10, NULL);
    INSERT INTO grades VALUES(4, 2, 9, '2016-05-10');
    INSERT INTO grades VALUES(5, 2, 8, NULL);
    INSERT INTO grades VALUES(6, 3, 6, '2016-05-26');
    INSERT INTO grades VALUES(7, 3, 7, '2016-05-27');

A)我想知道这是否是一个有效的解决方案,可以从一个日期字段(date)中获取最新记录,该字段来自于为每行中的每一行分组的辅助表(grades)主表(student)。

我的查询是:

    SELECT s.id, s.name, g.grade, g.date
    FROM student AS s
    LEFT JOIN (
        SELECT student_id, grade, DATE
        FROM grades AS gr
        WHERE DATE = (
            SELECT MAX(DATE)
            FROM grades
            WHERE student_id = gr.student_id
        )
        GROUP BY student_id
    ) AS g ON s.id = g.student_id

Sql Fiddle:http://sqlfiddle.com/#!9/a84171/2

此查询显示所需(几乎)结果。但我怀疑这是最好的方法,因为它看起来很难看,所以我对替代品非常好奇。

B)第二个问题是(almost)以上的原因,

对于第一行name=Jim,虽然我们有吉姆成绩,但它没有找到成绩。 因此,以防上述查询仅对NOT NULL日期字段有效。 问题是:

如何获得所有成绩的学生的最新成绩,包括Jim,即使他的成绩没有指定日期(NULL)。在这种情况下,最新的分组将由插入的最新行(MAX(id))或随机分配。

不能使用date = (SELECT...替换date IN (SELECT ...

非常感谢任何帮助,

谢谢!

[更新#1]:

对于B)我发现将这个添加到子查询OR date IS NULL,产生了所需的结果:

    SELECT s.id, s.name, g.grade, g.date
    FROM student AS s
    LEFT JOIN (
        SELECT id, student_id, grade, DATE
        FROM grades AS gr
        WHERE DATE = (
            SELECT MAX(DATE)
            FROM grades
            WHERE student_id = gr.student_id
        ) OR date IS NULL
        GROUP BY student_id
    ) AS g ON s.id = g.student_id

[更新#2]

如果一年级有学生的日期,似乎上一次更新有效。如果第一年级为空,则不会。我会联系一个小提琴,但似乎sqlfiddle现在不起作用。

所以这就是我到现在为止解决B)问题的方法:

    SELECT s.id, s.name, g.grade, g.date
    FROM student AS s
    LEFT JOIN (
        SELECT id, student_id, grade, DATE
        FROM grades AS gr
        WHERE (
            `date` = (
                SELECT MAX(DATE)
                FROM grades
                WHERE student_id = gr.student_id
            )
        ) OR (
            (
                SELECT MAX(DATE)
                FROM grades
                WHERE student_id = gr.student_id
            ) IS NULL AND 
            date IS NULL
        )   

    ) AS g ON s.id = g.student_id
    GROUP BY student_id

我仍然想知道你们是否知道更好的替代方案。

谢谢!

[更新#3]

@Strawberry 期望的结果将是:

    id  name    grade   date
    1   jim     10  NULL
    2   mark    9   2016-05-10
    3   john    7   2016-05-27
  • 每个学生都有一个相应的成绩
  • 如果成绩的日期存在,则获取最新成绩。

2 个答案:

答案 0 :(得分:1)

这个问题的复杂性源于没有相关日期的成绩的逻辑上的不可能性,所以显然解决方案是解决这个问题。

但这是一个解决方法......

E.g:

SELECT a.* 
  FROM grades a 
  JOIN 
     ( SELECT student_id
            , MAX(COALESCE(UNIX_TIMESTAMP(date),id)) date 
         FROM grades 
        GROUP 
           BY student_id
     ) b 
    ON b.student_id = a.student_id 
   AND b.date = COALESCE(UNIX_TIMESTAMP(a.date),id);

答案 1 :(得分:0)

http://sqlfiddle.com/#!9/ecec43/4

SELECT s.id, s.name, g.grade, g.date
FROM student AS s
LEFT JOIN (
  SELECT gr.student_id, gr.grade, gr.DATE
  FROM grades AS gr
  LEFT JOIN grades grm
  ON grm.student_id = gr.student_id 
    AND grm.date>gr.date
  WHERE grm.student_id IS NULL
    AND gr.date IS NOT NULL
  GROUP BY gr.student_id
) AS g 
ON s.id = g.student_id;