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

Bài 10: MEASUREMENT — Xét nghiệm & Đo lường

Ghi nhận kết quả xét nghiệm, sinh hiệu, đo lường lâm sàng. value_as_number / value_as_concept_id, operator_concept_id, unit_concept_id, range_low / range_high, LOINC vocabulary, measurement_event_id mới CDM 5.4.

🏗️ Kiến trúc — Bài 10 MEASUREMENT Xét nghiệm & Đo lường OMOP CDM 5.4 cho Người mới — Hiểu từ A đến Z Phần 3: Sự kiện lâm sàng chính xdev.asia

Dashboard xét nghiệm và đo lường y tế

Giới thiệu

MEASUREMENT là bảng ghi nhận mọi kết quả xét nghiệm, sinh hiệu, và đo lường lâm sàng — bất kỳ dữ liệu nào có giá trị đo được (số hoặc danh mục). Đây là bảng lớn nhất trong hầu hết database OMOP, thường chiếm >50% tổng record vì mỗi lần xét nghiệm tạo nhiều dòng (VD: bảng xét nghiệm máu 20 chỉ số = 20 records).


1. Cấu trúc bảng

CộtKiểuBắt buộcMô tả
measurement_idINTEGER✅ PKID duy nhất
person_idINTEGER✅ FKBệnh nhân
measurement_concept_idINTEGER✅Standard Concept (LOINC)
measurement_dateDATE✅Ngày xét nghiệm
measurement_datetimeDATETIMENgày giờ
measurement_timeVARCHAR(10)Giờ (legacy)
measurement_type_concept_idINTEGER✅Nguồn dữ liệu
operator_concept_idINTEGERToán tử (<, >, =, <=, >=)
value_as_numberFLOATGiá trị số
value_as_concept_idINTEGERGiá trị danh mục
unit_concept_idINTEGERĐơn vị (UCUM)
range_lowFLOATGiới hạn dưới bình thường
range_highFLOATGiới hạn trên bình thường
provider_idINTEGERFKBác sĩ yêu cầu
visit_occurrence_idINTEGERFKVisit liên quan
visit_detail_idINTEGERFKVisit detail
measurement_source_valueVARCHAR(50)Mã xét nghiệm gốc
measurement_source_concept_idINTEGERConcept gốc
unit_source_valueVARCHAR(50)Đơn vị gốc
unit_source_concept_idINTEGERConcept đơn vị gốc
value_source_valueVARCHAR(50)Giá trị gốc
measurement_event_idBIGINT⭐ CDM 5.4 mới
meas_event_field_concept_idINTEGER⭐ CDM 5.4 mới

2. value_as_number vs value_as_concept_id

2.1. Kết quả SỐ → value_as_number

-- Glucose máu: 6.5 mmol/L
INSERT INTO measurement VALUES (
    110001, 100001, 3004501,         -- LOINC: Glucose [Mass/volume] in Blood
    '2024-06-15', NULL, NULL,
    32817,                            -- EHR
    4172703,                          -- = (equals)
    6.5,                              -- value_as_number
    NULL,                             -- no concept value
    8753,                             -- UCUM: mmol/L
    3.9, 6.1,                         -- range: 3.9-6.1
    5001, 50001, NULL,
    'GLU', 0, 'mmol/L', 0,
    '6.5', NULL, NULL
);

2.2. Kết quả DANH MỤC → value_as_concept_id

-- Xét nghiệm nhóm máu: O
INSERT INTO measurement VALUES (
    110002, 100001, 3003694,         -- LOINC: ABO group [Type] in Blood
    '2024-06-15', NULL, NULL,
    32817,                            -- EHR
    NULL,                             -- no operator
    NULL,                             -- no numeric value
    36308332,                         -- Concept: Blood group O
    NULL,                             -- no unit
    NULL, NULL,                       -- no range
    5001, 50001, NULL,
    'BLOOD_TYPE', 0, NULL, 0,
    'O', NULL, NULL
);

2.3. Quy tắc chọn

Kết quảvalue_as_numbervalue_as_concept_id
"6.5 mmol/L"6.5NULL
"Dương tính"NULL4181412 (Positive)
"Âm tính"NULL4132135 (Negative)
"Nhóm máu O"NULL36308332
"> 100"100NULL + operator = >
"Bình thường"NULL4069590 (Normal)

3. operator_concept_id — Toán tử so sánh

Dùng khi kết quả không chính xác (VD: "< 0.5", "> 100").

Concept IDToán tửÝ nghĩa
4171756<Thấp hơn
4171754<=Thấp hơn hoặc bằng
4172703=Bằng (mặc định)
4172704>=Cao hơn hoặc bằng
4172702>Cao hơn
-- Kết quả HBsAg: < 0.05 (dưới ngưỡng phát hiện)
INSERT INTO measurement (
    measurement_id, person_id, measurement_concept_id,
    measurement_date, measurement_type_concept_id,
    operator_concept_id, value_as_number,
    unit_concept_id, measurement_source_value
) VALUES (
    110003, 100001, 3013721,         -- LOINC: HBsAg
    '2024-06-15', 32817,
    4171756,                          -- operator: <
    0.05,                             -- value
    8647,                             -- IU/mL
    'HBSAG'
);

4. LOINC — Vocabulary xét nghiệm

4.1. LOINC structure

LOINC Code = Component : Property : Time : System : Scale : Method
VD: 2345-7 = Glucose : MCnc : Pt : Ser/Plas : Qn

  Component  = Glucose         (đo gì)
  Property   = MCnc            (mass concentration)
  Time       = Pt              (point in time)
  System     = Ser/Plas        (huyết thanh/huyết tương)
  Scale      = Qn              (quantitative - số)

4.2. LOINC phổ biến

LOINC CodeConcept IDTênVN
2345-73004501Glucose [Mass/volume] in SerumĐường huyết
4548-43034639HbA1cHbA1c
2160-03016723Creatinine in SerumCreatinine máu
6768-63006923ALT in SerumSGPT
33914-33027018GFR estimatedeGFR
718-73000963HemoglobinHemoglobin
26515-73010813PlateletsTiểu cầu
2093-33027114Total CholesterolCholesterol toàn phần

5. unit_concept_id — Đơn vị UCUM

Concept IDUCUM CodeĐơn vị
8840mg/dLMilligrams per deciliter
8753mmol/LMillimoles per liter
8647IU/mLInternational units per milliliter
8554%Percent
9529kg/m2BMI
8876mm[Hg]Millimeters mercury
8582/uLPer microliter (blood cells)
8845mg/LMilligrams per liter

Quan trọng: Cùng một xét nghiệm nhưng đơn vị khác → giá trị khác. VD: Glucose 100 mg/dL = 5.6 mmol/L. ETL phải chuẩn hóa đơn vị!


6. measurement_event_id — Mới CDM 5.4

Cho phép liên kết measurement với bất kỳ event nào khác.

  ┌──────────────────────────────┐
  │     MEASUREMENT              │
  │  "Lab: BUN = 35 mg/dL"      │
  │  measurement_event_id = 70001│
  │  meas_event_field_concept_id │
  │  = 1147127                   │──→ condition_occurrence
  │     (condition_occurrence_id)│     .condition_occurrence_id
  └──────────────────────────────┘     = 70001

Ứng dụng: Liên kết xét nghiệm với chẩn đoán hoặc procedure cụ thể đã yêu cầu xét nghiệm đó.

-- Xét nghiệm BUN liên quan đến chẩn đoán suy thận
INSERT INTO measurement (
    measurement_id, person_id, measurement_concept_id,
    measurement_date, measurement_type_concept_id,
    value_as_number, unit_concept_id,
    measurement_event_id,
    meas_event_field_concept_id
) VALUES (
    110010, 100001, 3013682,         -- LOINC: BUN
    '2024-06-15', 32817,
    35, 8840,                         -- 35 mg/dL
    70001,                            -- Liên kết condition_occurrence_id = 70001
    1147127                           -- Field = condition_occurrence.condition_occurrence_id
);

7. ETL xét nghiệm VN

7.1. Vấn đề phổ biến

Vấn đềGiải pháp
Mã XN nội bộ BVMap qua SOURCE_TO_CONCEPT_MAP → LOINC
Kết quả text "Dương tính"Map sang value_as_concept_id
Kết quả "< 0.5"Tách operator + value_as_number
Đơn vị không chuẩnMap về UCUM
Range khác nhau theo labLấy range từ máy/lab, nhập range_low/range_high
Bảng XN 20 chỉ số = 1 dòngTách thành 20 measurement records

7.2. SQL ETL

-- Mỗi chỉ số xét nghiệm = 1 record MEASUREMENT
SELECT
    ROW_NUMBER() OVER() AS measurement_id,
    pm.person_id,
    COALESCE(stcm.target_concept_id, 0) AS measurement_concept_id,
    xn.ngay_xetnghiem AS measurement_date,
    32817 AS measurement_type_concept_id,
    -- Parse operator từ giá trị gốc
    CASE
        WHEN xn.gia_tri LIKE '<%' THEN 4171756    -- <
        WHEN xn.gia_tri LIKE '>%' THEN 4172702    -- >
        ELSE 4172703                                -- =
    END AS operator_concept_id,
    -- Parse số từ giá trị
    CASE
        WHEN xn.gia_tri ~ '^[<>]?[0-9.]+'
        THEN REGEXP_REPLACE(xn.gia_tri, '[^0-9.]', '', 'g')::FLOAT
        ELSE NULL
    END AS value_as_number,
    -- Kết quả danh mục
    CASE xn.gia_tri
        WHEN 'Dương tính' THEN 4181412    -- Positive
        WHEN 'Âm tính' THEN 4132135       -- Negative
        WHEN 'Bình thường' THEN 4069590   -- Normal
        ELSE NULL
    END AS value_as_concept_id,
    COALESCE(u.target_concept_id, 0) AS unit_concept_id,
    xn.gioi_han_duoi AS range_low,
    xn.gioi_han_tren AS range_high,
    xn.ma_xetnghiem AS measurement_source_value,
    xn.don_vi_goc AS unit_source_value,
    xn.gia_tri AS value_source_value
FROM xetnghiem_his xn
JOIN person_mapping pm ON xn.ma_bn = pm.source_id
LEFT JOIN source_to_concept_map stcm
    ON xn.ma_xetnghiem = stcm.source_code
    AND stcm.source_vocabulary_id = 'VN_LAB'
LEFT JOIN source_to_concept_map u
    ON xn.don_vi_goc = u.source_code
    AND u.source_vocabulary_id = 'VN_UNIT';

8. Sinh hiệu (Vital Signs) trong MEASUREMENT

Sinh hiệuLOINCConcept IDĐơn vị
Huyết áp tâm thu8480-63004249mmHg
Huyết áp tâm trương8462-43012888mmHg
Nhịp tim8867-43027018/min
Nhiệt độ8310-53020891°C
SpO259408-540762499%
Cân nặng29463-73025315kg
Chiều cao8302-23036277cm
BMI39156-53038553kg/m²
-- Ghi sinh hiệu: HA 130/85, NT 80, Nhiệt 37.2, SpO2 97%
INSERT INTO measurement (measurement_id, person_id, measurement_concept_id,
    measurement_date, measurement_type_concept_id,
    value_as_number, unit_concept_id, measurement_source_value)
VALUES
    (120001, 100001, 3004249, '2024-06-15', 32817, 130, 8876, 'SBP'),
    (120002, 100001, 3012888, '2024-06-15', 32817, 85, 8876, 'DBP'),
    (120003, 100001, 3027018, '2024-06-15', 32817, 80, 8541, 'HR'),
    (120004, 100001, 3020891, '2024-06-15', 32817, 37.2, 586323, 'TEMP'),
    (120005, 100001, 40762499, '2024-06-15', 32817, 97, 8554, 'SPO2');

9. SQL phân tích

-- Phân bố HbA1c trong dân số
SELECT
    CASE
        WHEN m.value_as_number < 5.7 THEN 'Bình thường (<5.7%)'
        WHEN m.value_as_number BETWEEN 5.7 AND 6.4 THEN 'Tiền ĐTĐ (5.7-6.4%)'
        WHEN m.value_as_number >= 6.5 THEN 'ĐTĐ (>=6.5%)'
    END AS hba1c_group,
    COUNT(DISTINCT m.person_id) AS patients,
    ROUND(AVG(m.value_as_number), 2) AS avg_hba1c
FROM measurement m
WHERE m.measurement_concept_id = 3034639  -- HbA1c
  AND m.value_as_number IS NOT NULL
  AND m.value_as_number BETWEEN 2 AND 20  -- loại outliers
GROUP BY hba1c_group
ORDER BY hba1c_group;

-- Xu hướng HbA1c theo thời gian (1 BN)
SELECT
    m.measurement_date,
    m.value_as_number AS hba1c,
    CASE
        WHEN m.value_as_number < 7.0 THEN '✅ Kiểm soát tốt'
        ELSE '⚠️ Cần cải thiện'
    END AS status
FROM measurement m
WHERE m.person_id = 100001
  AND m.measurement_concept_id = 3034639
ORDER BY m.measurement_date;

-- Xét nghiệm bất thường (ngoài range)
SELECT
    c.concept_name AS test_name,
    COUNT(*) AS abnormal_count,
    COUNT(DISTINCT m.person_id) AS patients
FROM measurement m
JOIN concept c ON m.measurement_concept_id = c.concept_id
WHERE m.value_as_number IS NOT NULL
  AND (m.value_as_number < m.range_low
       OR m.value_as_number > m.range_high)
  AND m.range_low IS NOT NULL
  AND m.range_high IS NOT NULL
GROUP BY c.concept_name
ORDER BY abnormal_count DESC
LIMIT 10;

Tổng kết

  1. MEASUREMENT = xét nghiệm + sinh hiệu + mọi giá trị đo được
  2. Hai loại kết quả: value_as_number (số) hoặc value_as_concept_id (danh mục)
  3. operator_concept_id cho kết quả "<" hoặc ">"
  4. unit_concept_id dùng UCUM, measurement_concept_id dùng LOINC
  5. range_low / range_high cho giá trị tham chiếu bình thường
  6. CDM 5.4: measurement_event_id liên kết xét nghiệm với event khác

Bài tiếp theo: OBSERVATION — sự kiện lâm sàng không thuộc các bảng chuyên biệt.


Tài liệu tham khảo