我正在尝试优化一个查询,该查询(不必要地)计算了表中近 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
Run Code Online (Sandbox Code Playgroud)
在此示例中,我当前的查询如下所示:
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;
Run Code Online (Sandbox Code Playgroud)
但此查询池了所有log_typeS IN内查询和限制是到1001行,这不是我想要的。我需要将行拆分为每个log_type,然后最多计算 1001 行。在这个例子中我想要的输出是:
log_type unread
'Type 1' 3
'Type 2' 2
Run Code Online (Sandbox Code Playgroud)
这个问题和这个问题讨论了如何在找到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
Run Code Online (Sandbox Code Playgroud)
请参阅DB Fiddle中的运行示例。
横向查询根据前面的表表达式上的可用值执行一次。在这种情况下,表表达式x将生成所有不同的值log_type(使用索引来提高性能)。然后横向查询将针对 中的每个值执行一次x,其中 aLIMIT为 3(在本例中)。最后,查询会计算z遇到的值的数量。
如您所见,上面的过程仅读取每种类型最多 3 行。