【问题标题】:Get count for each group, but stop counting after N result rows in each group获取每组的计数,但在每组中的 N 个结果行后停止计数
【发布时间】:2020-10-01 02:29:28
【问题描述】:

我正在尝试优化一个查询(不必要地)计算表中近 900 000 行的查询,这需要的时间太长。

该表包含发生在 Web 应用程序不同部分的事件的日志条目,我想知道当该类型的行数为 1000 或更少时,每种日志类型存在多少未读日志条目,但计数为如果计数为 1001 或更多,则最多为 1001 行。

之后我不需要再数了,我会为该日志类型输出“超过 1000 个”。

假设我们有一个名为my_logs 的带有数据的表:

id    log_type    log_text   is_read
1     'Type 1'    'Text 1'   1
2     'Type 1'    'Text 2'   1
3     'Type 1'    'Text 3'   0
4     'Type 1'    'Text 4'   0
5     'Type 1'    'Text 5'   0
6     'Type 1'    'Text 6'   0
7     'Type 2'    'Text 7'   0
8     'Type 2'    'Text 8'   0

在本例中,我当前的查询如下所示:

SELECT log_type, COUNT(*) AS unread FROM my_logs WHERE is_read = 0 GROUP BY log_type;

此查询计算每一行,并为每种日志类型提供正确的行数。问题是当表包含 900 000 行时,这是一个昂贵的查询,并且完全没有必要计算每种类型超过 1000 行,因为用户不会关心 1 000 和 20 000 之间的差异,他们只会查看很多条目

这是我最接近的解决方案(调整限制以适应 my_logs 示例并演示用法):

SELECT log_type, COUNT(*) AS unread
FROM (
    SELECT log_type
    FROM my_logs ml1
    WHERE is_read = 0
    LIMIT 3 /* To display "more than 2" in webapp */
) AS ml2
GROUP BY logtype_txt;

但是这个查询将内部查询中的所有log_types 汇集在一起​​,并将 that 限制为 1001 行,这不是我想要的。我需要将行拆分为每个 log_type,然后 然后 计算最多 1001 行。在这个例子中我想要的输出是:

log_type    unread
'Type 1'    3
'Type 2'    2

This questionthis question 讨论如何在找到 n 行时停止计数,但不要考虑我需要的分组。

有人知道解决办法吗?

【问题讨论】:

  • 澄清问题:您是否需要从您拥有的每种日志类型中仅获取 1000 个未读日志条目?你有多少种日志类型?
  • 我什至不需要日志条目;我只需要 count 每种日志类型有多少未读日志条目,并且我想在每种日志类型找到 1001 个条目时停止计数(一种日志类型可以有 514 个条目,而另一种则停止计数为 1001)。日志类型可以是我们突然决定使用的任何字符串,因此日志类型的数量是动态的。

标签: mysql mariadb


【解决方案1】:

此答案在 MariaDB 或 MySQL 中不起作用。

您正在寻找的答案是基于“侧表表达式”。这是在 Oracle、DB2、PostgreSQL 和 SQL Server 中实现的。

在 PostgreSQL 中,就从表中读取的行而言,这是最佳查询:

select x.log_type, count(y.z)
from (
  select distinct log_type as log_type
  from my_log
) x
left join lateral (
  select 1 as z
  from my_log b 
  where b.log_type = x.log_type and is_read = 0
  limit 2 + 1
) y on true
group by x.log_type

请参阅DB Fiddle 的运行示例。

横向查询根据放置在它们前面的表表达式上的可用值执行一次。在这种情况下,表表达式x 将为log_type 生成所有不同的值(使用索引来提高性能)。然后横向查询将针对来自x 的每个值执行一次,LIMIT 为 3(在这种情况下)。最后,查询计算遇到了多少z 值。

如您所见,上面的过程每个类型最多只能读取 3 行。

【讨论】:

  • 虽然这在我使用 MariaDB 时对我的具体情况没有帮助,但这是目前最好的答案。接受这是最佳答案,但我希望有人能够找到解决方法或让我知道在 MariaDB 中有效的解决方案。
  • 更新:MySQL 8.0.14 增加了对 LATERAL 关键字的支持。 MariaDB 是planning implementation in version 10.7
【解决方案2】:

查看 MariaDB-5.5.21 的LIMIT ROWS EXAMINED

https://mariadb.atlassian.net/browse/MDEV-28

这应该正是您所要求的。

(我认为它在 MySQL 中不可用。)

【讨论】:

  • 不幸的是,LIMIT ROWS EXAMINED 子句是 global for the whole statement,并且无法限制 GROUP BY 中每个单独组中的计数行数。不过,谢谢你的建议。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-15
  • 1970-01-01
  • 2014-06-30
  • 2015-03-10
  • 2015-09-11
相关资源
最近更新 更多