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

SQL多表关联查询去重求助:获取首次诊断记录

解决SQL获取首次诊断记录的重复问题及GROUP BY报错

刚接触SQL遇到这种问题太正常啦,我来一步步帮你拆解解决:

首先理解报错原因

你遇到的a.code未在GROUP BY中列出错误,是因为标准SQL要求SELECT语句里的列,要么出现在GROUP BY子句中,要么被聚合函数(比如MIN()、MAX()、COUNT()等)包裹。如果直接GROUP BY某些列,但SELECT里还有其他未被聚合的列,SQL不知道要取这个分组里的哪一条记录的该列值,所以会报错。

而你的核心需求是获取首次出现的诊断记录,直接GROUP BY去重不是最优解,我们需要先定位到每个诊断首次出现的时间点,再关联回原表获取完整信息。

假设表结构(基于你的描述合理推测)

先明确一下三张表的关联关系(如果和你的实际结构有出入,你可以微调字段名):

  • encounters:存储就诊记录,包含encounter_id(就诊ID)、patient_id(患者ID)、encounter_date(就诊日期)
  • diagnosis:存储诊断信息,包含diagnosis_id(诊断ID)、code(诊断编码)、description(诊断描述)
  • encounter_diagnosis:就诊与诊断的关联表,包含encounter_id、diagnosis_id

方法一:子查询+MIN()聚合找首次日期

这种方法是先找到每个患者+诊断组合的最早就诊日期,再关联回原表获取完整记录:

步骤1:获取每个患者+诊断的首次就诊日期

SELECT
  ed.patient_id,  -- 从encounters表关联获取患者ID
  ed.diagnosis_id,
  MIN(e.encounter_date) AS first_diagnosis_date
FROM encounter_diagnosis ed
JOIN encounters e ON ed.encounter_id = e.encounter_id
GROUP BY ed.patient_id, ed.diagnosis_id;

这里GROUP BY的是patient_id和diagnosis_id,确保我们是按「每个患者的每个诊断」来分组,用MIN()取该组最早的就诊日期,完全符合SQL标准,不会报错。

步骤2:关联原表获取完整诊断记录

把上面的子查询和三张表关联,筛选出对应首次日期的记录:

SELECT
  e.patient_id,
  d.code,
  d.description,
  e.encounter_date AS first_diagnosis_date,
  e.encounter_id
FROM encounters e
JOIN encounter_diagnosis ed ON e.encounter_id = ed.encounter_id
JOIN diagnosis d ON ed.diagnosis_id = d.diagnosis_id
-- 关联子查询得到的首次诊断日期
JOIN (
  SELECT
    ed_inner.patient_id,
    ed_inner.diagnosis_id,
    MIN(e_inner.encounter_date) AS first_date
  FROM encounter_diagnosis ed_inner
  JOIN encounters e_inner ON ed_inner.encounter_id = e_inner.encounter_id
  GROUP BY ed_inner.patient_id, ed_inner.diagnosis_id
) AS first_diag ON
  e.patient_id = first_diag.patient_id
  AND ed.diagnosis_id = first_diag.diagnosis_id
  AND e.encounter_date = first_diag.first_date;

这样就能得到每个患者每个诊断的首次出现记录,不会有重复。

方法二:窗口函数ROW_NUMBER()(更灵活)

如果你的SQL环境支持窗口函数(比如MySQL 8+、PostgreSQL、SQL Server等,备考大概率会涉及),用ROW_NUMBER()会更直观:

WITH ranked_diagnoses AS (
  SELECT
    e.patient_id,
    d.code,
    d.description,
    e.encounter_date,
    e.encounter_id,
    -- 按「患者+诊断」分组,按就诊日期升序排名,首次记录排名为1
    ROW_NUMBER() OVER (
      PARTITION BY e.patient_id, d.diagnosis_id
      ORDER BY e.encounter_date ASC
    ) AS rn
  FROM encounters e
  JOIN encounter_diagnosis ed ON e.encounter_id = ed.encounter_id
  JOIN diagnosis d ON ed.diagnosis_id = d.diagnosis_id
)
-- 只取排名为1的记录,也就是首次诊断
SELECT *
FROM ranked_diagnoses
WHERE rn = 1;

这里PARTITION BY相当于分组,ORDER BY决定排序规则,ROW_NUMBER()给每个组内的记录编号,取rn=1就是最早的那条诊断记录,逻辑清晰,也不会有GROUP BY的报错问题。

总结

  1. 避免直接GROUP BY去重的误区:必须保证SELECT的列要么在GROUP BY中,要么是聚合函数
  2. 获取首次记录的两种核心思路:
    • 先找首次时间,再关联回原表
    • 用窗口函数给记录排名,取排名第一的
  3. 备考时两种方法都要掌握,窗口函数是现在SQL的常用技巧,子查询是基础逻辑

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 10:27:11