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

SQL中结合时间条件关联实验室测量值,如何获取B测量对应值

获取测量B对应时间点值的SQL优雅实现

需求与现状

我有一组实验室测量数据集,需要完成以下操作:

  • 获取所有患者ID对应的测量A的数值及时间
  • 关联特定时间范围内(测量A时间的前90天内、且不晚于A时间)测量B的最早、最晚时间,以及对应的测量值

当前使用的SQL语句仅能获取测量B的最早、最晚时间,无法拿到对应的测量值,且希望找到无需再次关联同一张测量表的实现方式。

当前SQL语句:

SELECT  
    A.patient_id,   
    A.time AS A_time,  
    A.value AS A_value,
    MIN(B.time) AS earliest_B_time,
    MAX(B.time) AS latest_B_time
FROM  
    schema.measurements A 
LEFT JOIN
    schema.measurements B
    ON A.pat_id = B.pat_id
WHERE
    B.name= 'B'
    AND A.name = 'A'
    AND B.time <= A.time
    AND B.time >= (A.time - 90)
GROUP BY
    A.patient_id,   
    A.time,  
    A.value

解决方案

以下两种方法均无需多次关联原表,利用窗口函数或子查询一次扫描完成计算,属于高效优雅的实现:

方法一:使用FIRST_VALUE和LAST_VALUE窗口函数

适用于支持窗口函数的SQL方言(如PostgreSQL、MySQL 8.0+、SQL Server等),通过窗口函数直接在关联后的结果集中提取对应时间的测量值:

SELECT DISTINCT
    A.patient_id,
    A.time AS A_time,
    A.value AS A_value,
    -- 获取窗口内最早B时间对应的测量值
    FIRST_VALUE(B.value) OVER (
        PARTITION BY A.patient_id, A.time
        ORDER BY B.time ASC
        ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
    ) AS earliest_B_value,
    MIN(B.time) OVER (PARTITION BY A.patient_id, A.time) AS earliest_B_time,
    -- 获取窗口内最晚B时间对应的测量值
    LAST_VALUE(B.value) OVER (
        PARTITION BY A.patient_id, A.time
        ORDER BY B.time ASC
        ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
    ) AS latest_B_value,
    MAX(B.time) OVER (PARTITION BY A.patient_id, A.time) AS latest_B_time
FROM
    schema.measurements A
LEFT JOIN
    schema.measurements B
    ON A.patient_id = B.patient_id
    AND B.name = 'B'
    AND B.time <= A.time
    AND B.time >= (A.time - 90)
WHERE
    A.name = 'A'

注:添加DISTINCT是为了去除因多B记录关联导致的重复结果,确保每个测量A记录对应唯一的结果行。

方法二:使用ROW_NUMBER()标记目标记录后聚合

先在子查询中给每个患者的测量B记录按时间排序并标记序号,再通过条件聚合提取最早、最晚时间对应的测量值:

SELECT
    A.patient_id,
    A.time AS A_time,
    A.value AS A_value,
    MAX(CASE WHEN B.rn_asc = 1 THEN B.time END) AS earliest_B_time,
    MAX(CASE WHEN B.rn_asc = 1 THEN B.value END) AS earliest_B_value,
    MAX(CASE WHEN B.rn_desc = 1 THEN B.time END) AS latest_B_time,
    MAX(CASE WHEN B.rn_desc = 1 THEN B.value END) AS latest_B_value
FROM
    schema.measurements A
LEFT JOIN (
    SELECT
        patient_id,
        time,
        value,
        -- 按时间升序标记最早记录
        ROW_NUMBER() OVER (PARTITION BY patient_id ORDER BY time ASC) AS rn_asc,
        -- 按时间降序标记最晚记录
        ROW_NUMBER() OVER (PARTITION BY patient_id ORDER BY time DESC) AS rn_desc
    FROM
        schema.measurements
    WHERE
        name = 'B'
) B ON A.patient_id = B.patient_id
    AND B.time <= A.time
    AND B.time >= (A.time - 90)
WHERE
    A.name = 'A'
GROUP BY
    A.patient_id,
    A.time,
    A.value

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 19:25:28