MySql返回具有最高值的行,在日期或之前创建

时间:2016-07-19 09:13:38

标签: mysql

我有下表(SQFiddle, with sample data here):

|-------------------------------------------------------|
| id |data_date   | value | score |     created_at      |
|-------------------------------------------------------|
| 1  | 2015-01-01 | 10    |  10   | 2016-07-01 09:00:00 |
| 2  | 2015-02-01 | 10    |  10   | 2016-07-01 09:00:00 |
| 3  | 2015-03-01 | 10    |  10   | 2016-07-01 09:00:00 |
| 4  | 2015-01-01 | 15    |  20   | 2016-07-02 09:00:00 |
| 5  | 2015-03-01 | 15    |  20   | 2016-07-02 09:00:00 |
| 6  | 2015-03-01 | 15    |  15   | 2016-07-03 09:00:00 |
|-------------------------------------------------------|

我想做的是,为每个data_data返回一条记录,在给定的created_at日期获得最高分。

2016-07-02结果的预期结果为:

|-------------------------------------------------------|
| id |data_date   | value | score |     created_at      |
|-------------------------------------------------------|
| 4  | 2015-01-01 | 15    |  20   | 2016-07-02 09:00:00 |
| 2  | 2015-02-01 | 10    |  10   | 2016-07-01 09:00:00 |
| 5  | 2015-03-01 | 15    |  20   | 2016-07-02 09:00:00 |
|-------------------------------------------------------|

到目前为止,我所取得的成绩是为每个data_data返回单个最高得分记录,但我似乎无法添加正确的条件来考虑created_at字段。

select `my_table`.* 
from `my_table` 
left outer join `my_table` as `t2` 
on `my_table`.`data_date` = `t2`.`data_date` AND 
(
    (`my_table`.`score` < `t2`.`score`) OR 
    (`my_table`.`score` = `t2`.`score` AND `my_table`.`id` < `t2`.`id`)
) 
where `t2`.`data_date` is null 
order by `my_table`.`data_date` asc

返回以下结果:

|-------------------------------------------------------|
| id |data_date   | value | score |     created_at      |
|-------------------------------------------------------|
| 4  | 2015-01-01 | 15    |  20   | 2016-07-02 09:00:00 |
| 2  | 2015-02-01 | 10    |  10   | 2016-07-01 09:00:00 |
| 6  | 2015-03-01 | 15    |  15   | 2016-07-03 09:00:00 |
|-------------------------------------------------------|

使用如下所示的查询,获取在7月2日或之前创建的所有记录,得分最高:

select `my_table`.* 
from `my_table` 
left outer join `my_table` as `t2` 
on `my_table`.`data_date` = `t2`.`data_date` AND 
(
    (`my_table`.`score` < `t2`.`score`) OR 
    (`my_table`.`score` = `t2`.`score` AND `my_table`.`id` < `t2`.`id`)
) AND
DATE_FORMAT(my_table.created_at, '%Y-%m-%d') <= '2016-07-02' AND
DATE_FORMAT(t2.created_at, '%Y-%m-%d') <= '2016-07-02'
where `t2`.`data_date` is null 
order by `my_table`.`data_date` asc

返回以下错误结果(不应返回第6行):

|-------------------------------------------------------|
| id |data_date   | value | score |     created_at      |
|-------------------------------------------------------|
| 4  | 2015-01-01 | 15    |  20   | 2016-07-02 09:00:00 |
| 2  | 2015-02-01 | 10    |  10   | 2016-07-01 09:00:00 |
| 5  | 2015-03-01 | 15    |  20   | 2016-07-02 09:00:00 |
| 6  | 2015-03-01 | 15    |  15   | 2016-07-03 09:00:00 |
|-------------------------------------------------------|

总结

对于单个data_date,可能会有多条记录,每条记录都有不同的value和不同的score。我想为每个data_data返回单行,该score具有在给定日期或之前创建的最高data_date。即如果data_data在每周的每一天(周一至周五)创建了一条记录,我可能希望每周tk_getOpenFile获得最高得分值。

3 个答案:

答案 0 :(得分:1)

做这种事情的小提琴是(ab)使用GROUP_CONCAT函数。您可以按需要分组的字段进行分组,并在每个其他字段上使用GROUP_CONCAT,按您希望最大值的字段降序排序。这可以获得连接在一起的每个值的所有值。

然后,您可以使用SUBSTRING_INDEX获取每个值的第一个值。

SELECT SUBSTRING_INDEX(GROUP_CONCAT(id ORDER BY score DESC), ',', 1),
    data_date,
    SUBSTRING_INDEX(GROUP_CONCAT(value ORDER BY score DESC), ',', 1),
    SUBSTRING_INDEX(GROUP_CONCAT(score ORDER BY score DESC), ',', 1),
    SUBSTRING_INDEX(GROUP_CONCAT(created_at ORDER BY score DESC), ',', 1)
FROM my_table
GROUP BY data_date

在这个例子中很容易。当你有可能包含逗号的文本字段,或者你有NULL值时,它会变得有点复杂。

这可能会以更有效的方式为您提供现有结果。但我不确定你是如何尝试将created_at日期考虑在内,除非你的意思是你只想考虑某个created_at日期之前的所有记录: -

SELECT SUBSTRING_INDEX(GROUP_CONCAT(id ORDER BY score DESC), ',', 1),
    data_date,
    SUBSTRING_INDEX(GROUP_CONCAT(value ORDER BY score DESC), ',', 1),
    SUBSTRING_INDEX(GROUP_CONCAT(score ORDER BY score DESC), ',', 1),
    SUBSTRING_INDEX(GROUP_CONCAT(created_at ORDER BY score DESC), ',', 1)
FROM my_table
WHERE created_at <= '2016-07-02 23:59:59' 
GROUP BY data_date

编辑

第二个查询是在指定日期结束之前获取所有记录(我使用了这种方式的日期/时间,而不是从列中的值中提取日期部分,因为这允许日期的索引/ time要使用,同时提取日期部分可以防止使用任何索引,并且还强制在表的每一行上使用函数。对于所有匹配的记录,它按data_date字段对它们进行分组。对于id,value,score和created_at字段,它使用GROUP_CONCAT对每个data_date的所有值进行分组,每个值用逗号分隔(默认值),按分数降序排序。

在2015-01-01的data_date阶段,id字段将包含' 4,1 ',值将包含' 15,10 ',得分将包含' 20,10 ',created_at将包含' 2016-07-02 09:00:00,2016-07-01 09:00:00 '。< / p>

然后使用SUBSTRING_INDEX获取每个字段的第一个逗号的所有内容。由于它们按降序排列顺序,因此将获得与最高分相对应的每个值。

答案 1 :(得分:1)

SELECT x.*
  FROM my_table x
  JOIN
     ( SELECT a.data_date
            , a.created_at
            , MAX(a.score) score
         FROM my_table a
         JOIN
            ( SELECT data_date
                   , MAX(created_at) created_at
                FROM my_table
               WHERE created_at <= '2016-07-02 23:59:59'
               GROUP 
                  BY data_date
            ) b
           ON b.data_date = a.data_date
          AND b.created_at = a.created_at
        GROUP
           BY a.data_date
            , a.created_at
     ) y
    ON y.data_date = x.data_date
   AND y.created_at = x.created_at
   AND y.score = x.score;

答案 2 :(得分:-1)

以下查询将在您的案例中正常工作

select max(id), data_date, max(value), max(score), max(created_at) 
 from     
  my_table 
 where score in (select max(score) from my_table group by data_date) 
 group by data_date;