MAX() 和 GROUP BY,需要整个结果的 COUNT(*)

MAX() and GROUP BY, need COUNT(*) of whole result

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

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

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

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

unique_id是table的主键。

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

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

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 查询可能 return 数万行,所以我认为 WITH 获取总数的方法不是最好的方法。

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


示例数据

假设这是我的 table:

中的数据
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 查询会 return 这个(假设客户端指定 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(*) 从它,其中会 return 6,我会 return 6 和结果给客户。

MySQL 8 引入了对 window functions 的支持,包括 window 聚合函数。 Window 聚合函数允许您 return 聚合结果和非聚合数据。基本上,您可以通过附加 OVER 子句将常规聚合函数转换为 window 聚合函数,但通常您可能需要指定其他选项,这在链接手册。

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

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

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

并一次性得到原始明细数据和行数OVER 子句没有附加子句,这意味着它适用于整个行集。

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