PostgreSQL使用GROUP BY时如何处理JSON类型列的报错问题
报错原因
PostgreSQL的原生json类型仅做JSON格式存储,没有预定义相等、大小比较等运算符,当你把adress字段放到查询列且没有做聚合、也没有加入GROUP BY子句时,PostgreSQL会尝试校验非聚合列是否都在分组字段中,而如果把json类型的adress加入GROUP BY又会因为缺少相等运算符触发你遇到的报错。
另外你原本的SQL逻辑也不符合需求:GROUP BY id,name,age如果id是主键的话实际是按单条数据分组,无法实现「取每个年龄下薪资最高人员」的效果。
可行解决方案
该需求完全可以实现,以下是3种落地方式:
- 方案1:使用窗口函数实现需求(最推荐,完全适配你的业务场景)
不需要修改字段类型,也不需要对JSON字段做任何转换,直接按年龄分组取薪资最高的全量数据,还能兼容同年龄多人薪资同为最高的场景:
WITH ranked_person AS ( SELECT id, name, salary, age, adress, -- 按年龄分区,同年龄内按薪资倒序排名 RANK() OVER(PARTITION BY age ORDER BY salary DESC) AS salary_rk FROM test ) SELECT id, name, salary, age, adress FROM ranked_person WHERE salary_rk = 1;
- 方案2:将JSON字段转换为JSONB类型
jsonb是PostgreSQL支持的二进制JSON类型,内置相等比较运算符,支持分组、索引等操作。如果仅临时查询可以用转换写法:
-- 临时将json转jsonb参与分组 SELECT id, name, MAX(salary) AS max_salary, age, adress::jsonb FROM test GROUP BY id, name, age, adress::jsonb;
如果长期使用建议直接修改表结构,性能更高还支持更多JSON操作:
ALTER TABLE test ALTER COLUMN adress TYPE jsonb USING adress::jsonb;
- 方案3:将JSON字段转为text类型参与分组(不推荐,仅作兼容备选)
如果不方便用前两种方案,可以将adress转成文本类型参与分组,但需注意JSON格式缩进、键顺序不同可能导致相同语义的JSON转text后判定为不相等:
SELECT id, name, MAX(salary) AS max_salary, age, adress FROM test GROUP BY id, name, age, adress::text;
内容的提问来源于stack exchange,提问作者Rajeev
相关产品推荐
相关产品推荐

