根据 group by 查询 LAST N 行

Posted

技术标签:

【中文标题】根据 group by 查询 LAST N 行【英文标题】:Query LAST N rows based on group by 【发布时间】:2014-11-06 15:04:20 【问题描述】:

我的 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 的 LATEST 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

【问题讨论】:

Order by put 只对这种情况下的最终结果进行排序。我需要的是在分组之前对行进行排序。 在您的示例中,所有测量似乎都是同时进行的。也许如果你想要最后两行,你可以检查“created_at > 05-11-2014”。 (谁说 ORDER BY!??! ;-) )不管怎样,想要的结果是什么样的? 编辑了问题以包含所需的结果。 【参考方案1】:

您可以对结果进行排序,然后应用 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;

【讨论】:

【参考方案2】:

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

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

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 个字符。这可以更改,但根据数据量和所需的行数可能不是问题。

【讨论】:

以上是关于根据 group by 查询 LAST N 行的主要内容,如果未能解决你的问题,请参考以下文章

在pyspark数据框中根据group by连接行值

优化 PostgreSql 查询以获取找到的记录总数和基于多个 group by 的分页所需的有限行数

MySQL之分组查询(GROUP BY)

sql 第2行显示如何GROUP BY Last Name,然后ORDER BY两件事 - 首先是COUNT(OrderID),然后是LastName,在ASC ord中排序

在 GROUP BY 中按行数获取前 N 行

Mysql group by 同类产品根据关系如果有的话