☰
用 PostgreSQL GROUP BY 按类型统计记录数:count-records-by-type 实战指南
2026/10/7 9:55:10 网站建设 项目流程
  • 文档
  • 教程
  • 知识库

【免费下载链接】til

:memo: Today I Learned

项目地址:https://gitcode.com/gh_mirrors/ti/til
点击查看免费下载

本指南以仓库文档 postgres/count-records-by-type.md 为核心,讲解如何利用group by与聚合函数count(*)对带有类型列的数据库表进行分组统计。读完本文,你将掌握"按某列分类计数"这一最常用的数据探索手段,并进一步学会排序输出、处理 NULL 分组、按表达式分组、使用having过滤聚合结果等进阶写法,轻松应用于报表统计、去重排查与数据质量分析等场景。

一、核心问题:如何按类型统计记录数

在实际业务表中,我们经常会有某种"类型列"(type column)——例如订单表里的支付方式、日志表里的日志级别、商品表里的分类、宠物图鉴里的属性。最常见的需求是:这张表里每一种类型各有多少条记录?

答案非常简单:借助group by把记录按类型归组,再用聚合函数count(*)统计每一组的行数。原文档给出了一个经典的pokemon(宝可梦)示例:

> select type, count(*) from pokemon group by type; type | count ----------------- fire | 10 water | 4 plant | 7 psychic | 3 rock | 12

这里group by type会将pokemon表中所有type相同的行合并为一个分组,随后count(*)对每个分组内的行数进行计数。最终结果每一行代表一种type及其对应的记录条数,是典型的一对多分组统计。

与之完全同构的另一个例子来自仓库文档 postgres/count-how-many-records-there-are-of-each-type.md:一张books表通过status列区分书籍处于published、review、draft等状态,同样一条查询即可得到各状态的书目数量:

> select status, count(*) from books group by status; status | count -----------+------- ø | 123 published | 611 draft | 364 review | 239 (4 rows)

可以看到,两种写法完全一致:select 类型列, count(*) from 表名 group by 类型列就是"按类型统计记录数"的标准模板,可以套用在任何带有枚举、状态或分类性质的列上。

二、语法要点:SELECT 中的非聚合列必须出现在 GROUP BY 中

当select列表中同时出现普通列和聚合函数(如count(*)、sum、avg)时,PostgreSQL 要求:所有非聚合的普通列都必须出现在group by子句中,否则会直接报错。也就是说,select type, count(*) from pokemon group by type;中type既出现在投影列,又出现在分组键,二者保持一致,查询才能成立。

如果只写select type, count(*) from pokemon;(省略group by),PostgreSQL 会抛出错误提示:column "pokemon.type" must appear in the GROUP BY clause or be used in an aggregate function。这正是group by的语义保证——每一个输出行对应一个分组,而非对应单条记录。

三、进阶一:按计数降序排列,让结果更有秩序

默认情况下,group by输出的分组顺序并不保证稳定。仓库文档 postgres/count-how-many-records-there-are-of-each-type.md 指出,可以通过在查询末尾追加order by让输出按计数值排序,从而一眼看出数量最多的类型:

> select status, count(*) from books group by status order by 2 desc; status | count -----------+------- published | 611 draft | 364 review | 239 ø | 123 (4 rows)

这里的order by 2用的是**位置索引(argument index)**写法。PostgreSQL 中select列表里的每个参数都拥有一个从 1 开始的索引,可以在order by和group by中直接引用。所以order by 2等价于order by count(*),desc表示从高到低降序排列。这种技巧在 postgres/use-argument-indexes.md 中有更完整的介绍——例如:

select id, updated_at from posts order by 2;

就等价于order by updated_at。该文档还给出了一个同时使用位置索引完成"按类型分组 + 按计数降序"的组合示例:

select type, count(*) from transaction group by 1 order by 2 desc;

其中group by 1引用type,order by 2引用count(*)。在列名较长或聚合表达式复杂时,位置索引能让查询更紧凑;不过若调整了select列顺序,位置索引会随之改变,需谨慎使用。

四、进阶二:NULL 类型也自成一组

当类型列没有not null约束时,group by会把所有NULL值的记录归入同一个分组。上面books示例输出中的ø一行就是status为 NULL 的记录组,共计 123 条。这是group by的既定行为:NULL 作为一个特殊的分组键存在。

这一点对实际业务很有意义——统计结果中出现的 NULL 分组,往往意味着数据录入不完整或存在脏数据,可以据此定位需要补全或清洗的记录,而不仅仅是把它们忽略掉。

五、进阶三:按函数/表达式结果分组,扩展统计维度

group by并不局限于直接按某一列分组。仓库文档 postgres/group-by-the-result-of-a-function-call.md 展示了按表达式结果分组的做法。假设products表的identifier列前三个字母代表商品分类,可以这样按分类统计:

select substring(identifier from 1 for 3), count(*) from products group by substring(identifier from 1 for 3);

需要注意:group by中出现的表达式必须与select列表中的表达式逐字一致,PostgreSQL 不支持用别名(alias)引用分组表达式。这种写法把"按类型计数"推广到了"按类型的派生特征计数",例如按日期截断到月份统计订单数(group by date_trunc('month', created_at))、按首字母统计联系人数量等,非常适合数据探索。

六、进阶四:用 HAVING 过滤分组结果

如果你想要的不是全部分组,而是"计数满足某种条件的类型",则需要使用having子句。因为聚合函数(如count)不能在where中使用——where作用于分组之前的单行,而聚合结果要等分组完成后才产生。仓库文档 postgres/find-records-that-contain-duplicate-values.md 用它来查找重复邮箱:

select email, count(*) from mailing_list group by email having count(*) > 1 order by email;

这个查询按email分组,having count(*) > 1仅保留出现次数大于 1 的分组,从而精确筛出重复记录。同样的思路可以套用到类型统计上:例如只想看记录数超过某个阈值的类型,只需把having条件改为having count(*) > 100。where过滤行,having过滤分组,这是组合使用时的关键区分。

七、进阶五:按布尔条件计数,统计分组内的满足数

除了统计"每一组有多少行",还常常需要统计"每一组内有多少行满足某个条件"。仓库文档 postgres/count-the-number-of-trues-in-an-aggregate-query.md 给出了用case表达式配合sum的经典写法:

select author_id, sum(case when available then 1 else 0 end) from books group by author_id;

case在聚合前把布尔值转换为 1/0,sum累加后即为"可用书籍数"。统计不满足条件的数量时只需反转分支:

sum(case when available then 0 else 1 end)

这种"按类型计数"的变体在权限统计、状态分布、指标卡占比等场景中极为实用,可以与group by自由组合成多维度的统计查询。

八、小结:按类型统计的查询范式

综合原文档 postgres/count-records-by-type.md 及其在仓库中的系列姊妹篇,可以归纳出按类型统计记录数的完整范式:

需求SQL 写法关键点
每种类型各多少条select type, count(*) from t group by type;非聚合列必须在group by中
按计数从多到少排序追加order by 2 desc(或order by count(*) desc)可用位置索引引用select参数
NULL 类型也统计无需额外处理NULL 自动成为独立分组
按派生特征统计group by substring(...)、group by date_trunc(...)表达式须在select与group by中一致
只看满足条件的分组追加having count(*) > N聚合结果过滤必须用having
统计组内满足条件的行sum(case when ... then 1 else 0 end)case转 0/1 后聚合

掌握这一组写法后,任何带类型、状态、分类列的 PostgreSQL 表,都可以用几条简洁的 SQL 完成分布统计——这也是数据分析与报表开发中使用频率最高的基础能力之一。相关完整示例与进阶变体可继续在仓库 postgres/ 目录下查阅 count-how-many-records-there-are-of-each-type.md、use-argument-indexes.md 与 group-by-the-result-of-a-function-call.md 等文档。

  • 文档
  • 教程
  • 知识库

【免费下载链接】til

:memo: Today I Learned

项目地址:https://gitcode.com/gh_mirrors/ti/til
点击查看免费下载

相关推荐

上一篇:老游戏兼容工具 DDrawCompat:3 步让《星际争霸》《暗黑2》在 Win10/11 上满血复活
下一篇:老游戏画面模糊还闪退?DDrawCompat兼容工具从零上手完全指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询