Chuyển đến nội dung chính

Bài 15: CONCEPT_RELATIONSHIP & CONCEPT_ANCESTOR

Mối quan hệ giữa Concepts (Maps to, Is a, RxNorm has ingredient...) và cây phân cấp Ancestor-Descendant. Bảng quan trọng nhất cho ETL mapping và phân tích hierarchical.

🏗️ Kiến trúc — Bài 15 CONCEPT_RELATIONSHIP & CONCEPT_ANCESTOR OMOP CDM 5.4 cho Người mới — Hiểu từ A đến Z Phần 5: Standardized Vocabularies xdev.asia

Giới thiệu

Nếu CONCEPT là "từ điển", thì CONCEPT_RELATIONSHIP là "bản đồ" kết nối các từ lại với nhau, và CONCEPT_ANCESTOR là "cây gia phả" thể hiện quan hệ tổ tiên-hậu duệ. Hai bảng này cực kỳ quan trọng: CONCEPT_RELATIONSHIP dùng cho ETL (mapping ICD-10 → SNOMED), CONCEPT_ANCESTOR dùng cho phân tích (tìm tất cả mã "tiểu đường" bao gồm type 1, type 2, gestational...).


1. CONCEPT_RELATIONSHIP

1.1. Cấu trúc bảng

CộtKiểuMô tả
concept_id_1INTEGERFK → CONCEPT (nguồn)
concept_id_2INTEGERFK → CONCEPT (đích)
relationship_idVARCHAR(20)FK → RELATIONSHIP
valid_start_dateDATENgày bắt đầu
valid_end_dateDATENgày hết hạn
invalid_reasonVARCHAR(1)NULL/U/D

1.2. Relationship quan trọng nhất

relationship_idÝ nghĩaUse case
Maps toSource → StandardETL mapping (cốt lõi!)
Mapped fromStandard → SourceNgược lại Maps to
Is aCon → ChaPhân cấp SNOMED
SubsumesCha → ConNgược lại Is a
RxNorm has ingredientDrug → IngredientTìm hoạt chất
Has tradenameGeneric → BrandDrug mapping

1.3. "Maps to" — Quan trọng nhất cho ETL

  ICD10CM: E11 "Type 2 diabetes mellitus"
  concept_id = 45591837
  standard_concept = NULL (Non-standard)
       │
       │ relationship_id = 'Maps to'
       ↓
  SNOMED: "Type 2 diabetes mellitus"
  concept_id = 201826
  standard_concept = 'S' (Standard)
-- Tìm Standard Concept từ ICD-10 code
SELECT
    c1.concept_code AS source_code,
    c1.concept_name AS source_name,
    c1.vocabulary_id AS source_vocab,
    cr.relationship_id,
    c2.concept_id AS standard_concept_id,
    c2.concept_name AS standard_name,
    c2.vocabulary_id AS standard_vocab,
    c2.domain_id
FROM concept c1
JOIN concept_relationship cr
    ON c1.concept_id = cr.concept_id_1
    AND cr.relationship_id = 'Maps to'
    AND cr.invalid_reason IS NULL
JOIN concept c2
    ON cr.concept_id_2 = c2.concept_id
    AND c2.standard_concept = 'S'
    AND c2.invalid_reason IS NULL
WHERE c1.concept_code = 'E11'
  AND c1.vocabulary_id = 'ICD10CM';

1.4. "Is a" — Phân cấp SNOMED

  Diabetes mellitus (concept_id = 201820)
       ↑ Is a
  ├── Type 1 diabetes mellitus (201254)
  │        ↑ Is a
  │   ├── Type 1 DM without complication (435216)
  │   └── Type 1 DM with ketoacidosis (443727)
  │
  ├── Type 2 diabetes mellitus (201826)
  │        ↑ Is a
  │   ├── Type 2 DM without complication (443732)
  │   └── Type 2 DM with peripheral angiopathy (318712)
  │
  └── Gestational diabetes (4058243)
-- Tìm concept cha trực tiếp
SELECT
    c_parent.concept_id,
    c_parent.concept_name
FROM concept_relationship cr
JOIN concept c_parent ON cr.concept_id_2 = c_parent.concept_id
WHERE cr.concept_id_1 = 201826   -- Type 2 DM
  AND cr.relationship_id = 'Is a'
  AND cr.invalid_reason IS NULL;

-- Tìm concept con trực tiếp
SELECT
    c_child.concept_id,
    c_child.concept_name
FROM concept_relationship cr
JOIN concept c_child ON cr.concept_id_1 = c_child.concept_id
WHERE cr.concept_id_2 = 201826   -- Type 2 DM
  AND cr.relationship_id = 'Is a'
  AND cr.invalid_reason IS NULL;

2. Bảng RELATIONSHIP

CộtKiểuMô tả
relationship_idVARCHAR(20)PK
relationship_nameVARCHAR(255)Tên quan hệ
is_hierarchicalVARCHAR(1)1 = phân cấp
defines_ancestryVARCHAR(1)1 = tạo ancestor
reverse_relationship_idVARCHAR(20)Quan hệ ngược
relationship_concept_idINTEGERFK → concept

3. CONCEPT_ANCESTOR — Cây phân cấp đầy đủ

3.1. Cấu trúc bảng

CộtKiểuMô tả
ancestor_concept_idINTEGERFK → CONCEPT (tổ tiên)
descendant_concept_idINTEGERFK → CONCEPT (hậu duệ)
min_levels_of_separationINTEGERKhoảng cách tối thiểu
max_levels_of_separationINTEGERKhoảng cách tối đa

3.2. So sánh CONCEPT_RELATIONSHIP vs CONCEPT_ANCESTOR

CONCEPT_RELATIONSHIPCONCEPT_ANCESTOR
Nội dungQuan hệ trực tiếpTất cả tổ tiên-hậu duệ
Ví dụType 2 DM → Is a → DMType 2 DM → mọi ancestor
LevelsChỉ 1 bậcBao gồm n bậc
Dùng choETL mappingPhân tích hierarchical
Includes selfKhông✅ (min_level = 0)

3.3. Tại sao cần CONCEPT_ANCESTOR?

CONCEPT_RELATIONSHIP chỉ có quan hệ "1 bậc". Muốn tìm tất cả loại tiểu đường (type 1, type 2, gestational, neonatal...), phải duyệt cây nhiều lần. CONCEPT_ANCESTOR đã tính sẵn (pre-computed transitive closure).

-- Tìm TẤT CẢ concept thuộc nhóm "Diabetes mellitus"
-- Bao gồm bản thân + mọi hậu duệ
SELECT
    ca.descendant_concept_id,
    c.concept_name,
    ca.min_levels_of_separation AS levels
FROM concept_ancestor ca
JOIN concept c ON ca.descendant_concept_id = c.concept_id
WHERE ca.ancestor_concept_id = 201820   -- Diabetes mellitus
  AND c.standard_concept = 'S'
ORDER BY ca.min_levels_of_separation, c.concept_name;
-- Kết quả: ~300+ concepts bao gồm mọi loại & biến chứng

3.4. Ứng dụng phân tích

-- Đếm BN có BẤT KỲ loại tiểu đường nào
SELECT COUNT(DISTINCT co.person_id) AS dm_patients
FROM condition_occurrence co
WHERE co.condition_concept_id IN (
    SELECT descendant_concept_id
    FROM concept_ancestor
    WHERE ancestor_concept_id = 201820  -- Diabetes mellitus
);

-- So sánh: KHÔNG dùng ancestor (chỉ bắt 1 loại)
SELECT COUNT(DISTINCT co.person_id) AS dm_type2_only
FROM condition_occurrence co
WHERE co.condition_concept_id = 201826;  -- Chỉ Type 2 DM
-- → Bỏ sót Type 1, gestational, neonatal, with complications...!

4. SOURCE_TO_CONCEPT_MAP — Mapping tùy chỉnh

Dùng khi vocabulary chưa có mapping (VD: mã nội bộ BV Việt Nam).

CộtKiểuMô tả
source_codeVARCHAR(50)Mã nguồn
source_concept_idINTEGERConcept nguồn (0 nếu chưa có)
source_vocabulary_idVARCHAR(20)ID vocabulary tùy chỉnh
source_code_descriptionVARCHAR(255)Mô tả
target_concept_idINTEGERFK → Standard Concept
target_vocabulary_idVARCHAR(20)Vocabulary đích
valid_start_dateDATE
valid_end_dateDATE
invalid_reasonVARCHAR(1)
-- Tạo mapping cho mã ICD-10-VN nội bộ
INSERT INTO source_to_concept_map (
    source_code, source_concept_id,
    source_vocabulary_id, source_code_description,
    target_concept_id, target_vocabulary_id,
    valid_start_date, valid_end_date
) VALUES
    ('E11', 0, 'VN_ICD10',
     'Đái tháo đường type 2',
     201826, 'SNOMED',
     '2024-01-01', '2099-12-31'),
    ('METFORMIN500', 0, 'VN_DRUG',
     'Metformin 500mg viên nén',
     1503328, 'RxNorm',
     '2024-01-01', '2099-12-31');

5. CONCEPT_SYNONYM — Tên đồng nghĩa

CộtKiểuMô tả
concept_idINTEGERFK → CONCEPT
concept_synonym_nameVARCHAR(1000)Tên đồng nghĩa
language_concept_idINTEGERNgôn ngữ (4180186 = English)
-- Tìm concept qua tên đồng nghĩa
SELECT DISTINCT c.concept_id, c.concept_name
FROM concept_synonym cs
JOIN concept c ON cs.concept_id = c.concept_id
WHERE LOWER(cs.concept_synonym_name) LIKE '%heart attack%'
  AND c.standard_concept = 'S';
-- Tìm được: Acute myocardial infarction

6. Quy trình ETL Mapping hoàn chỉnh

  Bước 1: Lấy mã nguồn
  HIS: ma_benh = 'E11.65'
       │
  Bước 2: Tìm Source Concept
       │ SELECT * FROM concept
       │ WHERE concept_code = 'E11.65'
       │   AND vocabulary_id = 'ICD10CM'
       ↓
  Source: concept_id = 45591837
       │
  Bước 3: Tìm Maps to
       │ SELECT * FROM concept_relationship
       │ WHERE concept_id_1 = 45591837
       │   AND relationship_id = 'Maps to'
       ↓
  Standard: concept_id = 201826
            domain_id = 'Condition'
       │
  Bước 4: Domain routing
       │ domain_id = 'Condition'
       ↓
  Lưu vào: CONDITION_OCCURRENCE
            condition_concept_id = 201826
            condition_source_concept_id = 45591837
            condition_source_value = 'E11.65'

Tổng kết

  1. CONCEPT_RELATIONSHIP: quan hệ trực tiếp giữa 2 concepts
  2. "Maps to" = quan hệ quan trọng nhất cho ETL (Source → Standard)
  3. "Is a" = phân cấp SNOMED (con → cha)
  4. CONCEPT_ANCESTOR: pre-computed tất cả ancestor/descendant
  5. SOURCE_TO_CONCEPT_MAP: mapping tùy chỉnh cho mã nội bộ
  6. Luôn dùng CONCEPT_ANCESTOR khi phân tích hierarchical

Bài tiếp theo: DRUG_STRENGTH & các bảng Vocabulary còn lại.


Tài liệu tham khảo