PostgreSQL实现客户手机号行转列,解决单号码重复显示问题
问题描述
在Azure Data Studio中使用PostgreSQL设计客户信息数据库,客户最多可拥有两个不同手机号。执行SELECT *查询得到如下多行结果:
Name | Number James | 12344532 James | 23232422
期望将客户的两个手机号合并到同一行展示,格式如下(单个手机号的客户第二个号码字段留空):
Name | Number1 | Number2 James 12344532 23232422 John 32443322 Jude 12121212 23232422
尝试执行以下SQL语句:
SELECT name.name, min(details.number) AS number1, max(details.number) AS number2 FROM name JOIN details ON name.id=details.id GROUP BY name.name
但结果中仅有一个手机号的客户,两个号码字段会重复显示该号码:
Name | Number1 | Number2 James 12344532 23232422 John 32443322 32443322 Jude 12121212 23232422
解决方案
问题出在MIN()和MAX()聚合函数上:当客户只有一个手机号时,这两个函数返回的是同一个值,导致两个字段重复。可以通过窗口函数+条件聚合的方式解决,让第二个号码字段在无数据时返回NULL(展示为空)。
方法1:使用ROW_NUMBER()窗口函数+CASE条件
SELECT n.name, MAX(CASE WHEN rn = 1 THEN d.number END) AS number1, MAX(CASE WHEN rn = 2 THEN d.number END) AS number2 FROM name n JOIN ( -- 给每个客户的手机号按ID分组编号(1、2) SELECT id, number, ROW_NUMBER() OVER (PARTITION BY id ORDER BY number) AS rn FROM details ) d ON n.id = d.id GROUP BY n.name;
方法2:使用PostgreSQL专属的FILTER子句(更简洁)
SELECT n.name, MAX(d.number) FILTER (WHERE rn = 1) AS number1, MAX(d.number) FILTER (WHERE rn = 2) AS number2 FROM name n JOIN ( SELECT id, number, ROW_NUMBER() OVER (PARTITION BY id ORDER BY number) AS rn FROM details ) d ON n.id = d.id GROUP BY n.name;
原理说明
- 子查询中通过
ROW_NUMBER()给每个客户的手机号分配序号(PARTITION BY id按客户分组,ORDER BY number按手机号排序),序号为1和2。 - 外层聚合时,仅提取序号为1的手机号作为
number1,序号为2的作为number2;若客户只有一个手机号,序号2不存在,对应字段会返回NULL(展示为空),避免重复。
内容的提问来源于stack exchange,提问作者Jaymes
相关产品推荐
相关产品推荐

