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

PostgreSQL根据ID单查询获取人员及对应子表全字段方案求助

问题描述

我有一张主表T_PERSONNE,以及三张子表:T_EMPLOYE(员工)、T_PROSPECT(潜在客户)、T_VISITEUR(访客)。主表的PER_CATEG字段标识人员类别:"EMP"代表员工,"PROSP"代表潜在客户,"VIS"代表访客。主表存储公共字段,子表仅存各自独有字段,主表与对应子表的ID保持一致。

已知人员ID,如何用单条查询获取该人员的所有主表字段,以及对应类别子表的所有字段?

我尝试用CASE语句,但只能获取子表单个字段,代码如下:

SELECT *,
       CASE WHEN PER_CATEG='EMP' THEN (select * from T_EMPLOYE where EMP_ID = 1)
            WHEN PER_CATEG='PROSP' THEN (select * from T_PROSPECT where PROSP_ID = 1)
            WHEN PER_CATEG='VIS' THEN (select * from T_VISITEUR where VIS_ID = 1)
            ELSE FALSE
       END
FROM T_PERSONNE 
WHERE PER_ID = 1;

报错信息:the subquery must only return one column(子查询必须仅返回一列)


解决方案

错误原因

你之前的写法报错是因为CASE表达式的每个分支只能返回单个值/单个列,但你的子查询select *返回了子表的所有列,违反了这个限制,因此触发了报错。

方案1:LEFT JOIN + 条件映射(通用SQL)

这种写法兼容所有SQL数据库,通过左连接三个子表,再根据主表的PER_CATEG字段,只保留对应子表的字段值,其他子表字段会显示为NULL:

SELECT
    p.*,
    -- 员工子表字段
    CASE WHEN p.PER_CATEG = 'EMP' THEN e.EMP_FONCTION END AS EMP_FONCTION,
    CASE WHEN p.PER_CATEG = 'EMP' THEN e.EMP_DATE_EMBAUCHE END AS EMP_DATE_EMBAUCHE,
    CASE WHEN p.PER_CATEG = 'EMP' THEN e.EMP_SALAIRE_EURO END AS EMP_SALAIRE_EURO,
    CASE WHEN p.PER_CATEG = 'EMP' THEN e.EMP_EST_DIRECTEUR END AS EMP_EST_DIRECTEUR,
    -- 潜在客户子表字段
    CASE WHEN p.PER_CATEG = 'PROSP' THEN pr.PROSP_FONCTION END AS PROSP_FONCTION,
    CASE WHEN p.PER_CATEG = 'PROSP' THEN pr.PROSP_DATE_EMBAUCHE END AS PROSP_DATE_EMBAUCHE,
    CASE WHEN p.PER_CATEG = 'PROSP' THEN pr.PROSP_SALAIRE_EURO END AS PROSP_SALAIRE_EURO,
    CASE WHEN p.PER_CATEG = 'PROSP' THEN pr.PROSP_NOMBRE_TOTAL_VENTE END AS PROSP_NOMBRE_TOTAL_VENTE,
    CASE WHEN p.PER_CATEG = 'PROSP' THEN pr.PROSP_BENEFICE_TOTAL_VENTE_EURO END AS PROSP_BENEFICE_TOTAL_VENTE_EURO,
    CASE WHEN p.PER_CATEG = 'PROSP' THEN pr.PROSP_EST_DIRECTEUR END AS PROSP_EST_DIRECTEUR,
    -- 访客子表字段
    CASE WHEN p.PER_CATEG = 'VIS' THEN v.VIS_PROFESSION END AS VIS_PROFESSION,
    CASE WHEN p.PER_CATEG = 'VIS' THEN v.VIS_DATE_INSCRIPTION END AS VIS_DATE_INSCRIPTION,
    CASE WHEN p.PER_CATEG = 'VIS' THEN v.VIS_NOMBRE_VISITE END AS VIS_NOMBRE_VISITE,
    CASE WHEN p.PER_CATEG = 'VIS' THEN v.VIS_DATE_DERNIERE_VISITE END AS VIS_DATE_DERNIERE_VISITE,
    CASE WHEN p.PER_CATEG = 'VIS' THEN v.VIS_REVENU_EURO END AS VIS_REVENU_EURO
FROM T_PERSONNE p
LEFT JOIN T_EMPLOYE e ON p.PER_ID = e.EMP_ID
LEFT JOIN T_PROSPECT pr ON p.PER_ID = pr.PROSP_ID
LEFT JOIN T_VISITEUR v ON p.PER_ID = v.VIS_ID
WHERE p.PER_ID = 1;

方案2:PostgreSQL专属 - JSON聚合子表字段

如果使用PostgreSQL,可以将对应子表的整行数据转为JSON格式,结果更简洁,便于后续处理:

SELECT
    p.*,
    CASE p.PER_CATEG
        WHEN 'EMP' THEN to_json(e.*)
        WHEN 'PROSP' THEN to_json(pr.*)
        WHEN 'VIS' THEN to_json(v.*)
    END AS category_details
FROM T_PERSONNE p
LEFT JOIN T_EMPLOYE e ON p.PER_ID = e.EMP_ID AND p.PER_CATEG = 'EMP'
LEFT JOIN T_PROSPECT pr ON p.PER_ID = pr.PROSP_ID AND p.PER_CATEG = 'PROSP'
LEFT JOIN T_VISITEUR v ON p.PER_ID = v.VIS_ID AND p.PER_CATEG = 'VIS'
WHERE p.PER_ID = 1;

category_details字段会以JSON格式返回对应子表的所有字段,其他子表因为连接条件带了类别筛选,不会产生多余数据。

方案3:UNION ALL 拼接结果(适合批量查询)

如果需要查询多个人员,这种写法可以让结果更紧凑,每个人员只返回对应类别的字段:

SELECT
    p.*,
    e.EMP_FONCTION, e.EMP_DATE_EMBAUCHE, e.EMP_SALAIRE_EURO, e.EMP_EST_DIRECTEUR,
    NULL AS PROSP_FONCTION, NULL AS PROSP_DATE_EMBAUCHE, NULL AS PROSP_SALAIRE_EURO,
    NULL AS PROSP_NOMBRE_TOTAL_VENTE, NULL AS PROSP_BENEFICE_TOTAL_VENTE_EURO, NULL AS PROSP_EST_DIRECTEUR,
    NULL AS VIS_PROFESSION, NULL AS VIS_DATE_INSCRIPTION, NULL AS VIS_NOMBRE_VISITE,
    NULL AS VIS_DATE_DERNIERE_VISITE, NULL AS VIS_REVENU_EURO
FROM T_PERSONNE p
JOIN T_EMPLOYE e ON p.PER_ID = e.EMP_ID
WHERE p.PER_ID = 1

UNION ALL

SELECT
    p.*,
    NULL AS EMP_FONCTION, NULL AS EMP_DATE_EMBAUCHE, NULL AS EMP_SALAIRE_EURO, NULL AS EMP_EST_DIRECTEUR,
    pr.PROSP_FONCTION, pr.PROSP_DATE_EMBAUCHE, pr.PROSP_SALAIRE_EURO,
    pr.PROSP_NOMBRE_TOTAL_VENTE, pr.PROSP_BENEFICE_TOTAL_VENTE_EURO, pr.PROSP_EST_DIRECTEUR,
    NULL AS VIS_PROFESSION, NULL AS VIS_DATE_INSCRIPTION, NULL AS VIS_NOMBRE_VISITE,
    NULL AS VIS_DATE_DERNIERE_VISITE, NULL AS VIS_REVENU_EURO
FROM T_PERSONNE p
JOIN T_PROSPECT pr ON p.PER_ID = pr.PROSP_ID
WHERE p.PER_ID = 1

UNION ALL

SELECT
    p.*,
    NULL AS EMP_FONCTION, NULL AS EMP_DATE_EMBAUCHE, NULL AS EMP_SALAIRE_EURO, NULL AS EMP_EST_DIRECTEUR,
    NULL AS PROSP_FONCTION, NULL AS PROSP_DATE_EMBAUCHE, NULL AS PROSP_SALAIRE_EURO,
    NULL AS PROSP_NOMBRE_TOTAL_VENTE, NULL AS PROSP_BENEFICE_TOTAL_VENTE_EURO, NULL AS PROSP_EST_DIRECTEUR,
    v.VIS_PROFESSION, v.VIS_DATE_INSCRIPTION, v.VIS_NOMBRE_VISITE,
    v.VIS_DATE_DERNIERE_VISITE, v.VIS_REVENU_EURO
FROM T_PERSONNE p
JOIN T_VISITEUR v ON p.PER_ID = v.VIS_ID
WHERE p.PER_ID = 1;

因为已知人员ID只会匹配其中一个分支,所以最终只会返回一行数据。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 07:24:56