2. 临床数据模型与SQL映射
在临床研究队列构建中,数据来源往往不是单一系统,而是电子病历(EHR)、专病登记库(Registry)和医保理赔数据(Claims)三类异构数据并存。它们各自服务于不同的业务目的,因此表结构、主键策略、时间粒度和编码体系差异显著。理解这些差异,是设计SQL映射与关联方案的前提。
三类数据源的表结构特征
电子病历数据以“患者—就诊—临床事件”为主线组织。典型结构包括:患者主表(patient),存储病案号、姓名、性别、出生日期等;就诊表(encounter/visit),记录每次门诊或住院的起止时间、科室、就诊类型;诊断表(diagnosis),通常一行一个诊断,含ICD编码、诊断类型(入院/出院/主诊断);医嘱或用药表(medication_order),记录药品名称、剂量、频次、起止时间;检验表(lab_result),含检验项目、结果值、单位、参考范围、标本采集时间。EHR的核心优势是临床细节丰富,但编码不规范、时间戳缺失、文本字段多。
登记库数据通常围绕特定疾病或手术建立,结构更规整。常见表包括:登记主表(registry_case),含登记编号、入组日期、随访状态;基线信息表(baseline),记录人口学、危险因素、分期等;随访表(follow_up),按随访时点存储疗效、生存状态、不良事件。登记库的字段经过预先定义,缺失率相对可控,但覆盖人群有限,且不同登记库之间字段命名和编码不统一。
Claims数据以“理赔—服务—费用”为核心。典型表结构为:会员表(member),含保险ID、参保类型;理赔主表(claim_header),记录理赔单号、服务起止日期、总费用;理赔明细表(claim_line),一行一个服务项目,含CPT/HCPCS或ICD-9/10手术编码、费用、数量;药品理赔表(pharmacy_claim),含NDC药品码、天数供应量。Claims数据覆盖全人群、时间连续,但缺乏临床检验值和疾病严重程度,且存在编码偏移和滞后。
主题域划分与SQL表关联设计
无论数据来源如何,临床研究队列通常需要围绕以下主题域组织:患者域(人口学、标识)、就诊域(住院/门诊事件)、诊断域(疾病编码与分期)、用药域(药品、剂量、暴露窗口)、检验域(实验室指标、影像报告)、结局域(死亡、复发、再入院)。SQL映射的核心任务,是把三类数据源中的物理表,映射到这些逻辑主题域,并建立可关联的键。
以“患者域”为例,EHR的patient表以病案号为主键,Registry的registry_case以登记号为主键,Claims的member以保险ID为主键。三者之间没有天然外键,必须通过患者主索引(EMPI)或确定性匹配规则(如身份证号+姓名+出生日期)建立映射表patient_crosswalk。该表至少包含:global_patient_id、source_system、local_patient_id、match_confidence。后续所有主题域表都通过global_patient_id挂载,形成以患者为锚点的星型结构。
“就诊域”的映射需要处理粒度差异。EHR的encounter表天然是就诊级,一次住院一行;Claims的claim_header也是理赔级,但一次住院可能拆分为多条理赔记录;Registry的follow_up是随访级。统一策略是定义临床事件表clinical_event,字段包括:event_id、global_patient_id、event_type(住院/门诊/随访/理赔)、start_date、end_date、source_system、source_record_id。EHR的encounter直接插入;Claims的claim_header按服务起止日期聚合后插入;Registry的follow_up按随访日期插入。这样,后续诊断、用药、检验都可以挂到统一的event_id上。
编码归一化与时间轴对齐
诊断域是编码差异最突出的地方。EHR常用ICD-10临床版,Claims常用ICD-10-CM或ICD-9-CM,Registry可能使用自定义分期码。SQL映射时,建议建立concept_diagnosis维表,将不同来源的编码映射到统一的研究概念码。例如,研究定义“2型糖尿病”时,EHR的E11.9、Claims的250.00、Registry的DM-2都映射到CONCEPT_DM2。映射表字段包括:source_system、source_code、concept_code、concept_name、valid_from、valid_to。
用药域需要处理药品名称、NDC码、ATC码之间的映射。EHR的medication_order通常存药品通用名和商品名,Claims的pharmacy_claim存NDC码。可以建立drug_crosswalk,将NDC映射到ATC或研究用药物分类。同时,用药暴露窗口的计算依赖起止日期,EHR的医嘱起止时间往往精确到分钟,Claims的药品理赔只有配药日期和天数供应量,需要按fill_date + days_supply推算结束日期。
检验域在Claims中几乎缺失,主要依赖EHR和Registry。EHR的lab_result表通常一行一个检验项目,需要按patient_id + encounter_id + lab_item_code + specimen_time关联。Registry的基线检验值可能直接存在baseline表的宽表中,需要做逆透视(unpivot)转为长表,才能与EHR的检验结果合并。
实操示例:构建统一患者事件表
以下SQL示例展示如何将EHR就诊、Claims理赔、Registry随访统一到clinical_event表。假设已存在patient_crosswalk映射表。
INSERT INTO clinical_event
(global_patient_id, event_type, start_date, end_date, source_system, source_record_id)
SELECT
cw.global_patient_id,
'EHR_ENCOUNTER' AS event_type,
e.admit_date,
e.discharge_date,
'EHR' AS source_system,
e.encounter_id AS source_record_id
FROM encounter e
JOIN patient_crosswalk cw
ON cw.source_system = 'EHR'
AND cw.local_patient_id = e.patient_id
UNION ALL
SELECT
cw.global_patient_id,
'CLAIM' AS event_type,
ch.service_start_date,
ch.service_end_date,
'CLAIMS' AS source_system,
ch.claim_id AS source_record_id
FROM claim_header ch
JOIN patient_crosswalk cw
ON cw.source_system = 'CLAIMS'
AND cw.local_patient_id = ch.member_id
UNION ALL
SELECT
cw.global_patient_id,
'REGISTRY_FOLLOWUP' AS event_type,
fu.visit_date,
fu.visit_date,
'REGISTRY' AS source_system,
fu.follow_up_id AS source_record_id
FROM follow_up fu
JOIN registry_case rc ON rc.case_id = fu.case_id
JOIN patient_crosswalk cw
ON cw.source_system = 'REGISTRY'
AND cw.local_patient_id = rc.registry_id;
执行后,clinical_event表将三类来源的事件按患者和时间轴排列。研究者可以进一步按global_patient_id聚合,计算每位患者的就诊次数、理赔总费用、随访完成率等指标。需要注意的是,Claims的服务日期通常滞后于实际就诊日期,时间轴对齐时应保留source_system字段,以便在分析时按来源分层或做敏感性分析。
global_patient_id?② 事件粒度是否统一到event_id?③ 编码映射表是否覆盖所有来源系统?④ 时间字段是否区分“发生时间”与“记录时间”?⑤ 跨源关联是否保留来源标识以便溯源?
三类数据源的SQL映射不是一次性工作,而是随着研究队列定义迭代调整。建议将映射逻辑封装为视图或物化表,并保留原始表与映射表的血缘关系,便于后续审计和复现。