【问题标题】:SQL query to count rows grouped by an ID, but limit count on each groupSQL 查询以计算按 ID 分组的行数,但限制每个组的计数
【发布时间】:2019-11-14 21:17:56
【问题描述】:

所以我有一个不寻常的要求。我正在处理一个有数十亿行的表。

该表有一列“id”不是唯一的,并且有一列“数据”

我想要做的是对按“id”分组的行数进行计数,但将计数限制为仅 150 个条目。我只需要知道任何给定的 id 是否有 150 行。

这是为了优化查询和性能。

它不一定是计数。我只需要知道给定的 id 是否有 150 个条目,而不需要 MySQL 在查询期间继续计数条目。如果这有意义的话。

我知道如何计数,我知道如何分组,并且我知道如何同时做这两个,但是计数会返回数以百万计的数字,这浪费了处理时间,并且查询需要运行数百数以千计的 ID。

【问题讨论】:

  • 顺便说一句,有一个名为 id 的列很奇怪,它不是唯一的
  • 我的表中有一个不同的 id 列是唯一的。我在这个问题中省略了它,因为它不相关。
  • PRIMARY KEY 是什么?

标签: mysql sql


【解决方案1】:

你无法真正优化性能——我不这么认为。

select id, (count(*) >= 150)
from t
group by id;

如果您碰巧有一个 单独的表,每个 id 有一行,并且在t(id) 上有一个索引,那么这可能会更快:

select ids.id,
       ((select count(*)
         from t
         where t.id = ids.id
        ) >= 150
       )
from ids;

不幸的是,MySQL 不支持相关子查询的双重嵌套,所以这是不可能的:

select ids.id,
       ((select count(*)
         from (select 1
               from t
               where t.id = ids.id
               limit 150
              ) t
        ) >= 150
       )
from ids;

如果是这样,这可能会更快。

编辑:

如果您在id 上有一个索引并且只想要具有 150 或更多的 id,那么变量可能会更快:

select id,
       (@rn := if(@id = id, @rn + 1,
                  if(@id := id, 1, 1)
                 )
       ) as rn
from (select id
      from t
      order by id
     ) t cross join
     (select @id := 0, @rn := 0) params
having rn = 150;

这里的想法是,使用索引对表进行排序、物化和再次扫描可能比group by 更快。我不认为row_number() 具有相同的性能特征。

编辑二:

上面的一个细微的变化可以用来获取所有带有标志的 id:

select id, (max(id) = 150)
from (select id,
             (@rn := if(@id = id, @rn + 1,
                        if(@id := id, 1, 1)
                       )
             ) as rn
      from (select id
            from t
            order by id
           ) t cross join
           (select @id := 0, @rn := 0) params
      having rn in (1, 150)
     ) t
group by id;

编辑三:

啊!如果您有一个单独的 id 表,那么这可能是最好的方法:

select ids.id,
       (select id
        from t
        where t.id = ids.id
        limit 1 offset 149
       ) is not null
from ids;

这将从索引中获取第 150 行。如果不存在,则不返回任何行。

【讨论】:

  • 最后一个例子你不需要rn >= 150吗?
  • @尼克。 . .不。每一行都被枚举,所以它只需要第 150 行。
  • 最后一个建议是迄今为止最好的。哇,我得到结果的速度比以前快了 75%。
  • 我应该算出 LIMIT 子句的值倒退了。先偏移,后限制。
  • @Cyber​​power678 。 . .谢谢。
【解决方案2】:

我认为这是不可能的。您将必须扫描整个表才能知道哪些ids 至少有150 个条目。

所以:

select id
from mytable
group by id
having count(*) >= 150

使用id 上的索引,这应该尽可能高效。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-06-25
    • 2019-09-08
    • 2016-12-07
    • 1970-01-01
    • 1970-01-01
    • 2012-12-31
    • 1970-01-01
    相关资源
    最近更新 更多