如何在 SQL 中查找模式,即 MAX(COUNT(*)) 不起作用

171 阅读3分钟

如何在 SQL 中查找模式,即 MAX(COUNT(*)) 不起作用

在统计中,模式是返回在一系列中最常出现的元素的函数。这就是人们在运行 MAX(COUNT(*))... 时试图实现的目标,但他们发现它不起作用。让我们看看如何在 SQL 中获取模式。

让我们像往常一样举一个例子。假设我们需要找出公司中哪个部门的员工最多。

SELECT department_id, COUNT(*) AS how_many
    FROM employee
    GROUP BY department_id
    ORDER BY how_many DESC
    LIMIT 1;

我们在这里所做的是获取唯一的最少部门,获取每个部门的员工人数,按(升序)员工人数对行进行排序,然后只取第一个。

WITH TIES

但是,多个部门可以有相同数量的员工,但上面的查询只能返回一行。如果我们想获得员工人数最多的所有部门,我们可以使用这个查询:

SELECT department_id, COUNT(*) AS how_many
    FROM employee
    GROUP BY department_id
    ORDER BY how_many DESC
    FETCH FIRST 1 ROWS WITH TIES;

FETCH FIRST n ROWS WITH TIES语法意味着我们当然想要第一行,但是接下来的行对于需要返回的列n具有相同的值ORDER BY(在这种情况下,相同)。COUNT(*)

支持 WITH TIES

但是,并非所有 DBMS 都支持这种语法。它支持:

  • PostgreSQL
  • 玛丽亚数据库
  • SQL 服务器
  • 甲骨文

它不支持:

  • MySQL
  • SQLite
  • 数据库2
  • 红移
  • 雪花

替代方法

在不支持的 DBMS 上WITH TIES,您可以查看替代方法,其中包括子查询和窗口函数。

使用子查询获取所有模式

SELECT department_id, COUNT(*) AS how_many
    FROM employee
    GROUP BY department_id
    HAVING how_many = (
        SELECT COUNT(*) AS how_many
            FROM employee
            GROUP BY department_id
            ORDER BY how_many DESC
            LIMIT 1
    );

在这里,我们:

  • 使用本文中的第一个查询来获取部门中最多的员工数(子查询HAVING)。
  • 再次计算每个部门的员工人数。
  • 从这个统计数据中,我们只取员工人数等于最高员工人数的行。

使用窗口函数方法获取所有模式

虽然上述方法有效,但语法有点过于冗长,而且速度可能很慢。我们可以使用窗口函数。

SELECT department_id, how_many AS how_many
    FROM (
        SELECT
                department_id,
                COUNT(*) AS how_many,
                RANK() OVER (ORDER BY COUNT(*) DESC) AS department_rank
            FROM employee
            GROUP BY department_id
    ) s
    WHERE department_rank = 1;

我们所做的是:

  • 按部门对员工进行分组,统计每个部门的员工人数。
  • 为每一行分配一个排名,从最高计数 ( ORDER BY COUNT(*) DESC) 开始。
  • 请注意,其中的行COUNT(*)相同,排名也将相同。
  • 从该子查询的结果中,选择排名较低(最高计数)的行。

为什么 MAX(COUNT(*)) 不起作用

SELECT MAX(COUNT(*)) FROM table GROUP BY column 不适用于任何 DBMS

原因是聚合函数(如MAX()和COUNT())接受任意数量的行作为输入并返回一个值作为输出。如果使用,输入行是每个组的行GROUP BY,否则是所有表行。

在这种情况下,COUNT()将获取每个组的行并返回该组的值。您期望MAX()如此,但这不是您正在编写的内容:您将聚合结果传递给它。此操作是不可能的。

表现

所有示例都需要在 上的索引,或者第一列为 的索引。否则至少会执行一次全表扫描(顺序读取),对于大表,这个操作很慢。department_iddepartment_id

要返回多种模式,该WITH TIES方法是最快的。它大致与第一个查询一样快,最多返回一行。

窗口函数方法会很快。

子查询方法可能很慢,因为GROUP BY它运行了两次。请注意,这LIMIT 1不会使 aGROUP BY更快,因为数据库在读取所有相关行之前无法返回正确的结果。