【问题标题】:MAX() and GROUP BY, need COUNT(*) of whole resultMAX() 和 GROUP BY,需要整个结果的 COUNT(*)
【发布时间】:2018-09-11 14:33:16
【问题描述】:

我正在执行一个SELECT 查询,其中有一个MAX() 列和一个GROUP BY 子句,除了我需要返回给客户端的查询结果之外,我还需要同时返回所有结果的总数。

基本上我的查询是这样的:

SELECT unique_id, col1, col2, MAX(col3) as col3
FROM tbl
GROUP BY col1, col2

它通常也会有一个WHERE 子句。

unique_id 是表的主键。

当将此数据返回给客户端时,我还指定了LIMITOFFSET 子句来限制一次检索的结果数量。我的问题是,如果上面的查询没有 LIMITOFFSET 子句,我还需要显示结果的总数,以便客户端可以稍后/增量检索其余部分。

我知道我可以轻松地使用 WITH 临时表来获得我想要的:

WITH temp AS (
    SELECT unique_id, col1, col2, MAX(col3) as col3
    FROM tbl
    GROUP BY col1, col2
)
SELECT count(*) FROM temp

但我担心这样做的效率。 sans-LIMIT-and-OFFSET 查询可以返回数万行,所以我认为获取总数的WITH 方法并不是最好的方法。

有没有更有效的方法,我没有想到?还是WITH 方法很好(例如,MySQL 服务器“智能”到足以不分配查询的整个结果集来获取计数)?


示例数据

假设这是我表中的数据:

unique_id  col1  col2  col3
___________________________
1          5     8     30
2          5     8     33
3          5     9     40
4          6     8     30
5          6     8     31
6          6     8     32
7          6     9     39
8          7     8     33
9          7     8     32
10         8     8     34

所以我的SELECT 查询会返回这个(假设客户端指定LIMIT 4 OFFSET 0):

SELECT unique_id, col1, col2, max(col3) as col3
FROM tbl
GROUP BY col1, col2
LIMIT 4
OFFSET 0;
    unique_id  col1  col2  col3
    ___________________________
    2          5     8     33
    3          5     9     40
    6          6     8     32
    7          6     9     39

然后我会使用该查询没有 LIMITOFFSET 子句作为子查询和 SELECT COUNT(*) 从它,这将返回 6,我会返回6 和客户端的结果。

【问题讨论】:

  • 你用的是什么版本的 MySQL?
  • @GordonLinoff MySQL 8
  • 您没有按unique_id 分组,但您正在返回它。 MySQL 确实有一种模式允许您选择非 GROUP BY 列作为非聚合列,但这并不意味着将它用于您在这里所做的事情是一个好主意。您无法可靠地预测在这种情况下将返回哪个 unique_id,那么为什么首先返回它呢?
  • 看起来您需要每个 col1col2 的最大 col3 并且还需要具有这 3 个值的 uniqe_id。你确认了吗?
  • @AndriyM 我明白你的意思。我们将unique_id 返回到 GUI 的唯一原因是我们可以也许稍后引用它,并且由于我们正在分组,返回的组的特定 unique_id,虽然确实不可预知,真的没那么重要。只要我们可以引用其中一个,我们就可以引用整个组。

标签: mysql sql mysql-8.0


【解决方案1】:

MySQL 8 引入了对window functions 的支持,包括窗口聚合函数。窗口聚合函数允许您将聚合结果与非聚合数据一起返回。基本上,您可以通过附加OVER 子句将常规聚合函数转换为窗口聚合函数,但通常您可能需要指定其他选项,链接手册中对此进行了详细说明。

您也可以在GROUP BY 查询中使用窗口聚合函数。在这些情况下,窗口聚合函数将在分组完成后应用于行集。另请注意,添加LIMIT 不会影响窗口聚合函数的结果。

考虑到以上所有因素,您可以像这样修改原始查询:

SELECT
  unique_id,
  col1,
  col2,
  MAX(col3) as col3,
  COUNT(*) OVER () AS TotalRows
FROM
  tbl
GROUP BY
  col1,
  col2
LIMIT
  4 OFFSET 0
;

一次性获取原始明细数据和行数。 OVER 子句没有额外的子句,这意味着它适用于整个行集。

正如我所说,窗口聚合函数将忽略 LIMIT 子句,如果一个附加到查询。因此,上面的TotalRows 列将反映行数,就好像没有应用限制一样。

【讨论】:

  • 太棒了,效果很好。而且EXPLAIN的输出是完全一样的,所以没有效率损失,只是结果集中额外的整数列增加了内存。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-23
  • 2019-03-21
  • 2021-02-25
  • 1970-01-01
  • 1970-01-01
  • 2021-11-07
相关资源
最近更新 更多