根据group by查询最后N行

时间:2014-11-06 15:04:21

标签: mysql sql performance database-performance

我的MySQL数据库中有以下表格:

Table temperature

id - sensor_id - value - created_at

1      1          4.5    04-11-2014
2      1          2.2    05-11-2014
3      1          3.3    06-11-2014
4      2          4.5    04-11-2014
5      2          2.2    05-11-2014
6      2          3.3    06-11-2014

我要做的是为每个sensor_id获取最新的N行。

我设法找到了许多不同的解决方案,但其中大多数都包含非常低效的连接,在我的情况下是不够的,因为我有100万行+并且查询非常慢。

最接近有效查询的是:

set @num := 0, @sensor_id:= '';

select id, sensor_id, value, created_at,
  @num := if(@sensor_id = sensor_id, @num + 1, 1) as row_number,
  @sensor_id := sensor_id as dummy
from temperature
group by id, sensor_id, value, created_at
having row_number <= 2;

此查询来自本文http://www.xaprb.com/blog/2006/12/07/how-to-select-the-firstleastmax-row-per-group-in-sql/,但问题是它需要FIRST N,而不是LATEST N行。如何引入ORDER BY,以获取最新的,而不是前N行?

如果查询最新的2行,则所需的结果应如下所示:

id - sensor_id - value - created_at

2      1          2.2    05-11-2014
3      1          3.3    06-11-2014
5      2          2.2    05-11-2014
6      2          3.3    06-11-2014

3 个答案:

答案 0 :(得分:2)

您可以订购结果,然后应用row_number逻辑

set @num := 0, @sensor_id:= '';

select *,
  @num := if(@sensor_id = sensor_id, @num + 1, 1) as row_number,
  @sensor_id := sensor_id as dummy
from
(select id, sensor_id, value, created_at
from temperature
order by sensor_id, created_at desc) T
group by id, sensor_id, value, created_at

having row_number <= 2;

答案 1 :(得分:0)

select id, sensor_id, value, created_at,
  @num := if(@sensor_id = sensor_id, @num + 1, 1) as row_number,
  @sensor_id := sensor_id as dummy
 from my_table, (select @num := 0, @sensor_id:= '') vars
group by id DESC, sensor_id, value, created_at 
having row_number <= 2;

答案 2 :(得分:0)

如果由于行数导致连接效率低,那么使用用户变量也可能效率低,因为查询需要检查每一行。

如果您在返回结果后稍微处理结果以获得您想要的格式,那么还有另一种选择。

SELECT sensor_id, SUBSTRING_INDEX(GROUP_CONCAT(CONCAT_WS(':', id, sensor_id, value, created_at) ORDER BY created_at DESC SEPARATOR '#'). '#', 4)
FROM temperature
GROUP BY sensor_id

这是使用CONCAT_WS将行中的所有值一起滚动,用“:”分隔。然后,它使用GROUP_CONCAT将所有这些值连接在一起,用于单个传感器ID,以#降序排列日期顺序(假设日期是实际日期格式,而不是文本dd-mm-yyyy格式)。最后,SUBSTRING_INDEX用于仅获取最后N行数据(在本例中我刚刚使用了4行)。如果您访问的数据包含任何“:”或“#”字符,则可以轻松使用其他分隔符。

返回此内容后,您需要将每个返回的行拆分回其各自的字段。

请注意,GROUP_CONCAT结果的最大长度(我认为)默认为1024个字符。这可以更改,但根据数据量和所需的行数可能不是问题。