在按另一个字段分组时,如何正确选择字符串的最大出现次数?

Tud*_*dor 7 postgresql postgresql-9.0

我正在使用 Postgresql 9.0。我在表中有以下字段:id, name.

 id    name 
 1     John
 1     Mary
 1     Mary
 1     Mary
 1     John
 1     Mary
 3     Paul
 3     Paul
 3     George
 .     .
 .     .
Run Code Online (Sandbox Code Playgroud)

对于每个id,我想选择出现次数最多的名称。我怎样才能做到这一点?

我尝试使用以下查询,但它不起作用:

select id, max(name) 
from table 
group by id;
Run Code Online (Sandbox Code Playgroud)

ype*_*eᵀᴹ 9

这不是小事。首先,您需要按 id 和 name 分组并计算行数:

SELECT COUNT(*)
...
GROUP BY id, name
Run Code Online (Sandbox Code Playgroud)

然后为每个 id 选择最大计数。实现此目的的一种方法是通过窗口函数。该RANK()函数:

RANK() OVER (PARTITION BY id ORDER BY COUNT(*) DESC)
Run Code Online (Sandbox Code Playgroud)

为结果的每一行分配一个数字(在分组完成后),将它们(行)排列在具有相同id和排序依据的COUNT(*) DESC分区中,因此对于每个 (partition of) id,具有最大计数的行是分配的等级为 1。因此,我们需要将上述内容放在派生表中,并使用WHERE条件仅保留这些行:

WHERE rnk = 1
Run Code Online (Sandbox Code Playgroud)

最后的查询是这样的:

SELECT
    id, name, cnt
FROM
    ( SELECT id, name, COUNT(*) AS cnt,
             RANK() OVER (PARTITION BY id ORDER BY COUNT(*) DESC) AS rnk
      FROM tableX
      GROUP BY id, name
    ) AS tg 
WHERE
    rnk = 1 ;
Run Code Online (Sandbox Code Playgroud)

SQL-Fiddle测试


请注意,如果您首先有联系(两个或更多名称具有相同的最大计数),则所有这些都将被返回。如果您希望最终结果中每个 id 严格为一行,则必须使用 theROW_NUMBER()而不是 theRANK()并可能更改ORDER BY子句以明确选择如何解决关系:

ROW_NUMBER() OVER (PARTITION BY id ORDER BY COUNT(*) DESC, name ASC) AS rnk
Run Code Online (Sandbox Code Playgroud)

测试:SQL-Fiddle test-2