如何正确编写PostgreSQL统计公司用户占比的查询语句?
错误原因说明
你提交的SQL存在3个核心问题,触发语法报错:
- 括号没有闭合:计算百分比的子查询只写了左括号
(,没有对应的右括号闭合,PostgreSQL解析到语句末尾未找到合法终止标识,就会报终止符相关的语法错误,哪怕你没有写;也会触发该类报错 - 同层级别名引用非法:在同一条SELECT语句中,不能直接引用刚定义的字段别名
nbre_utilisateurs计算其他字段,SQL执行顺序中SELECT字段解析晚于聚合逻辑,别名无法在同层级复用 - 子查询逻辑错误:子查询内不存在
nbre_utilisateurs这个别名,且不需要额外关联companies表、分组就能计算总用户数,逻辑完全冗余错误
实现方案
方案一:窗口函数实现(性能最优)
PostgreSQL原生支持窗口函数,该方案只需要扫描一次关联后的表就能出结果,是执行效率最高的写法:
SELECT c.name AS societe, COUNT(u.id) AS nbre_utilisateurs, ROUND(COUNT(u.id) * 100.0 / SUM(COUNT(u.id)) OVER (), 2) AS pourcentage FROM companies c LEFT JOIN users u ON c.id = u.company_id GROUP BY c.id, c.name ORDER BY nbre_utilisateurs DESC;
说明:
- 使用
LEFT JOIN会保留没有用户的公司,对应计数为0,如果你不需要保留无用户的公司,换成INNER JOIN即可 - 乘
100.0是为了触发浮点运算避免整数截断,ROUND第二个参数控制保留的小数位数,可按需调整 - 窗口函数
SUM(COUNT(u.id)) OVER ()会直接在聚合后的结果集上计算全局用户总数,不需要额外子查询
方案二:CTE预计算总用户数(兼容性更好)
该写法兼容更多低版本数据库,逻辑更易懂,适合新手理解:
-- 预计算全局总用户数 WITH total_user_count AS ( SELECT COUNT(*) AS total FROM users ) SELECT c.name AS societe, COUNT(u.id) AS nbre_utilisateurs, ROUND(COUNT(u.id) * 100.0 / tc.total, 2) AS pourcentage FROM companies c LEFT JOIN users u ON c.id = u.company_id CROSS JOIN total_user_count tc GROUP BY c.id, c.name, tc.total ORDER BY nbre_utilisateurs DESC;
性能对比
窗口函数的方案执行速度更快:
- 窗口函数仅需要对两张表做一次关联、一次聚合、一次窗口计算就能出结果
- CTE方案需要多扫描一次users表计算总用户数,表数据量越大,两种方案的性能差距越明显
- 如果给users表的
company_id字段加索引,两种方案的执行速度都会有大幅提升
内容的提问来源于stack exchange,提问作者René
相关产品推荐
相关产品推荐

