2. OMOP CDM表结构精讲
OMOP CDM 的表结构并不是按“业务系统模块”来划分的,而是按临床事件类型来划分的。理解这一点,是读懂 PERSON、OBSERVATION_PERIOD、VISIT_OCCURRENCE、CONDITION_OCCURRENCE、DRUG_EXPOSURE、MEASUREMENT、PROCEDURE_OCCURRENCE 这七张核心表的前提。它们共同构成了一条从“人”到“时间窗”再到“具体临床事件”的层次结构:PERSON 定义谁,OBSERVATION_PERIOD 定义这个人在什么时间段内被观察,其余五张事件表则记录在这段时间内发生了什么。
一、PERSON:一切事件的锚点
PERSON 表是 OMOP CDM 的根表,每一行代表一个自然人。它的关键字段包括:person_id(唯一主键,通常由 ETL 生成的整数,而非原始病历号)、gender_concept_id、year_of_birth、month_of_birth、day_of_birth、race_concept_id、ethnicity_concept_id、location_id、provider_id、care_site_id 以及 person_source_value。
这里有一个非常重要的设计原则:性别、种族、民族等字段必须使用标准概念 ID,而不是原始文本。例如性别应映射到 SNOMED 的 8507(Male)或 8532(Female),种族映射到 8527(White)、8516(Black or African American)等。出生日期允许部分缺失,但 year_of_birth 是必填的,因为几乎所有后续分析都需要年龄分层。person_source_value 用于保留源系统的患者标识,便于回溯核对,但不参与跨表关联。
二、OBSERVATION_PERIOD:观察时间窗
OBSERVATION_PERIOD 表定义每个患者在数据库中“被观察”的时间区间,字段为 observation_period_id、person_id、observation_period_start_date、observation_period_end_date 和 period_type_concept_id。它的作用是回答“这个人在什么时间段内的数据是完整的”。
在实际 ETL 中,观察期通常由该患者所有事件的最早日期和最晚日期推导而来,也可以由保险覆盖期、注册期决定。一个患者可以有多段观察期,例如中间有脱保或转院。需要特别注意的是:任何事件表的记录日期都应落在某个观察期之内,否则在队列研究和发病率计算时会被排除。这是数据质量检查中最常见的规则之一。
三、VISIT_OCCURRENCE:就诊事件
VISIT_OCCURRENCE 记录患者的一次就诊或住院,字段包括 visit_occurrence_id、person_id、visit_concept_id、visit_start_date、visit_end_date、visit_type_concept_id、provider_id、care_site_id、visit_source_value 以及 preceding_visit_occurrence_id。
visit_concept_id 使用标准就诊类型概念,例如 9201(Inpatient Visit)、9202(Outpatient Visit)、9203(Emergency Room Visit)。preceding_visit_occurrence_id 是一个自引用外键,用于串联同一患者的连续就诊,形成就诊链。这张表是其他事件表的重要上下文:CONDITION_OCCURRENCE、DRUG_EXPOSURE、MEASUREMENT、PROCEDURE_OCCURRENCE 都通过 visit_occurrence_id 关联到具体就诊,从而支持“这次住院期间发生了什么”的分析。
四、四张临床事件表的字段语义
CONDITION_OCCURRENCE 记录诊断或症状,核心字段为 condition_occurrence_id、person_id、condition_concept_id、condition_start_date、condition_end_date、condition_type_concept_id、condition_status_concept_id、visit_occurrence_id 和 condition_source_value。诊断概念通常来自 SNOMED CT,condition_status_concept_id 用于区分入院诊断、出院诊断、主诊断等。
DRUG_EXPOSURE 记录药物暴露,字段包括 drug_exposure_id、person_id、drug_concept_id、drug_exposure_start_date、drug_exposure_end_date、quantity、days_supply、sig、route_concept_id、drug_type_concept_id 和 visit_occurrence_id。药物概念来自 RxNorm,days_supply 和 quantity 是计算用药依从性(如 PDC、MPR)的关键参数。
MEASUREMENT 记录实验室检查、生命体征等结构化测量结果,字段包括 measurement_id、person_id、measurement_concept_id、measurement_date、value_as_number、value_as_concept_id、unit_concept_id、range_low、range_high、measurement_type_concept_id 和 visit_occurrence_id。测量概念来自 LOINC,单位来自 UCUM。例如血糖可映射为 LOINC 2339-0,单位 mg/dL 对应 UCUM 的 mg/dL。
PROCEDURE_OCCURRENCE 记录手术、操作和诊疗程序,字段包括 procedure_occurrence_id、person_id、procedure_concept_id、procedure_date、procedure_type_concept_id、quantity、visit_occurrence_id 和 procedure_source_value。手术概念通常来自 SNOMED CT 或 CPT4。
五、表间关联关系
这七张表通过 person_id 形成一对多关系:一个 PERSON 对应多个 OBSERVATION_PERIOD、多个 VISIT_OCCURRENCE 和多个临床事件。VISIT_OCCURRENCE 又通过 visit_occurrence_id 将事件表组织到具体就诊中。所有事件表都包含 *_type_concept_id 字段,用于说明该记录的来源类型(如 EHR、理赔、登记),这是 OMOP 中追溯数据来源的重要机制。
六、实操中的关联查询示例
假设我们要查询某患者在一次住院期间使用的所有药物及其对应的诊断,SQL 结构如下:
SELECT p.person_id,
v.visit_occurrence_id,
c.condition_concept_id,
d.drug_concept_id,
d.days_supply
FROM person p
JOIN visit_occurrence v ON p.person_id = v.person_id
JOIN condition_occurrence c ON v.visit_occurrence_id = c.visit_occurrence_id
JOIN drug_exposure d ON v.visit_occurrence_id = d.visit_occurrence_id
WHERE p.person_id = 1001
AND v.visit_concept_id = 9201
AND c.condition_start_date BETWEEN v.visit_start_date AND v.visit_end_date;
这条查询体现了 OMOP 的核心使用模式:以 PERSON 为锚点,以 VISIT_OCCURRENCE 为上下文,将不同事件表按时间窗和就诊 ID 对齐。实际执行时,还需要检查 condition_start_date 是否落在观察期内,以及 drug_exposure_end_date 是否合理(不能早于开始日期)。
*_concept_id 是标准化后的概念编码,*_source_value 是原始值;*_type_concept_id 说明数据来源;visit_occurrence_id 是事件与就诊的桥梁;person_id 是所有表的公共外键。
person_source_value 当作关联键使用,或在事件表中直接写入未标准化的文本诊断。OMOP 要求所有分析字段使用标准概念 ID,源值仅用于审计和回溯。