我正在尝试优化一个查询,这个查询(不必要地)在一个表中计算将近90万行,这花费了太长的时间。
该表包含发生在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
在本例中,我的当前查询如下所示:
选择log_type,从my_logs中将(*)计数为未读,其中is_read=0 GROUP BY log_type;
该查询对每一行进行计数,并给出每种日志类型的正确行数。 问题是,当表包含90万行时,这是一个昂贵的查询,每种类型的行数超过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_type
集合在一起,并将其限制为1001行,这不是我想要的。 我需要将行拆分为每个log_type
,然后计算最多1001行。 在本例中,我想要的输出是:
log_type unread
'Type 1' 3
'Type 2' 2
这道题和这道题讨论了当找到n行时如何停止计数,但不要考虑到我需要的分组。
有人知道解决办法吗?
这个答案在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
中的每个值执行一次,限制
为3(在本例中)。 最后,查询会计算遇到多少z
值。
如您所见,上面的进程最多只读取每个类型的3行。