1

I have a SELECT query I'm executing which has a MAX() column and a GROUP BY clause, and in addition to the results of this query that I need to return to the client, I also need to return the total count of all results as well.

Essentially my query is this:

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

It'll usually have a WHERE clause too.

The unique_id is the primary key of the table.

When returning this data to the client, I also specify LIMIT and OFFSET clauses to limit the number of results being retrieved at one time. My problem is that I also need to display the total count of results that the above query would produce if it didn't have the LIMIT and OFFSET clauses, so that the client can later/incrementally retrieve the rest.

I know I can easily use a WITH temporary table to get what I want:

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

But I'm concerned about the efficiency of this. The sans-LIMIT-and-OFFSET query could return tens of thousands of rows, so I'm thinking that the WITH approach to getting the total count isn't the best way to do this.

Is there a more efficient way, which I'm not thinking of? Or is the WITH method the fine (e.g. is the MySQL server "smart" enough to not allocate the entire result set of the query to get the count)?


Example data

Assume this is the data in my 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

So my SELECT query would return this (assume the client specified 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

And then I'd use that query without the LIMIT and OFFSET clauses as a subquery and SELECT COUNT(*) from it, which would return 6, and I'd return both the 6 and the results to the client.

4

1 回答 1

3

MySQL 8 引入了对窗口函数的支持,包括窗口聚合函数。窗口聚合函数允许您将聚合结果与非聚合数据一起返回。基本上,您可以通过附加一个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上面的列将反映行数,就好像没有应用限制一样。

于 2018-09-14T13:32:59.570 回答