You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从重复行执行查询?多表关联行转列实现求助

解决一对多关联导致的多行结果问题

嘿,我来帮你搞定这个问题!你遇到的是一对多关联查询里非常常见的重复行问题——因为一个人可能对应多条电话记录(家庭、工作各一条),直接JOIN后每条电话都会生成一行结果。要把同一人的不同类型电话合并到同一行,核心思路就是行转列,用「条件聚合」的方法几乎所有数据库都能支持,下面给你详细拆解:

先明确关键表结构假设(你可以对应自己的实际表调整)

我先默认你的表有这些核心字段(如果和你的实际结构不一样,替换成对应的字段名就行):

  • person表:person_id(主键)、name(对应你要的NAME)、mrn_nb(对应MRN_NB)
  • phone表:phone_id(主键)、person_id(外键关联person表)、phone_type(用来区分电话类型,比如'HOME'代表家庭电话,'WORK'代表工作电话)、phone_number(电话号码)

通用SQL写法(支持MySQL、SQL Server、PostgreSQL等绝大多数数据库)

SELECT
    p.name AS NAME,
    p.mrn_nb AS MRN_NB,
    -- 筛选出家庭电话,用MAX聚合忽略NULL值
    MAX(CASE WHEN ph.phone_type = 'HOME' THEN ph.phone_number END) AS HOME_PHONE_NBR,
    -- 筛选出工作电话
    MAX(CASE WHEN ph.phone_type = 'WORK' THEN ph.phone_number END) AS WORK_PHONE_NBR
FROM person p
-- 用LEFT JOIN保证没有电话的人也能出现在结果里
LEFT JOIN phone ph ON p.person_id = ph.person_id
-- 按person的唯一标识分组,确保每个人只返回一行
GROUP BY p.person_id, p.name, p.mrn_nb;

代码逻辑解释

  1. LEFT JOIN:哪怕某个人没有家庭或工作电话,依然会出现在结果中,对应的电话字段会显示为NULL,不会漏掉数据。
  2. CASE WHEN:根据电话类型匹配对应的号码,非目标类型的记录会返回NULL,相当于给不同类型的电话做标记。
  3. MAX()聚合函数:配合GROUP BY,把同一人的多条记录合并成一行,自动过滤掉NULL值,只保留有效的电话号码。
  4. GROUP BY:必须包含person表的主键和所有非聚合字段(比如name、mrn_nb),这样才能保证每个人员只返回一条结果。

针对不同数据库的简化写法

如果你的数据库有专用的行转列工具,还能简化代码:

  • MySQL:如果一个人可能有多个同类型电话(比如两个家庭电话),可以用GROUP_CONCAT把它们合并成逗号分隔的字符串:
SELECT
    p.name AS NAME,
    p.mrn_nb AS MRN_NB,
    GROUP_CONCAT(DISTINCT CASE WHEN ph.phone_type = 'HOME' THEN ph.phone_number END SEPARATOR ', ') AS HOME_PHONE_NBR,
    GROUP_CONCAT(DISTINCT CASE WHEN ph.phone_type = 'WORK' THEN ph.phone_number END SEPARATOR ', ') AS WORK_PHONE_NBR
FROM person p
LEFT JOIN phone ph ON p.person_id = ph.person_id
GROUP BY p.person_id, p.name, p.mrn_nb;
  • SQL Server:可以用PIVOT运算符实现行转列:
SELECT NAME, MRN_NB, HOME AS HOME_PHONE_NBR, WORK AS WORK_PHONE_NBR
FROM (
    -- 先整理出基础的人员+电话数据
    SELECT p.name AS NAME, p.mrn_nb AS MRN_NB, ph.phone_type, ph.phone_number
    FROM person p
    LEFT JOIN phone ph ON p.person_id = ph.person_id
) AS SourceTable
-- 把phone_type的不同值转成列
PIVOT (
    MAX(phone_number)
    FOR phone_type IN (HOME, WORK)
) AS PivotTable;
  • PostgreSQL:用FILTER子句简化条件聚合逻辑:
SELECT
    p.name AS NAME,
    p.mrn_nb AS MRN_NB,
    MAX(ph.phone_number) FILTER (WHERE ph.phone_type = 'HOME') AS HOME_PHONE_NBR,
    MAX(ph.phone_number) FILTER (WHERE ph.phone_type = 'WORK') AS WORK_PHONE_NBR
FROM person p
LEFT JOIN phone ph ON p.person_id = ph.person_id
GROUP BY p.person_id, p.name, p.mrn_nb;

小提示

如果你的phone表没有phone_type字段,而是用其他方式区分电话类型(比如单独的is_home布尔字段),只需要调整CASE WHEN里的判断条件就行,比如改成CASE WHEN ph.is_home = 1 THEN ph.phone_number END。

内容的提问来源于stack exchange,提问作者Jessica Gibbons

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 09:03:45