Bạn có thể bỏ qua phần này và làm OMOP — nhưng analytic sẽ luôn sai. Vocabulary là phần làm cho OMOP có ý nghĩa thật. Bài viết deep dive Concept, hierarchy, mapping và Athena workflow.
1. Vì sao vocabulary quan trọng
Ví dụ thực tế: bạn có 3 nguồn dữ liệu:
- BV A: code chẩn đoán "ICD-10 E11.9"
- BV B: code "ICD-10 E11"
- BV C: code SNOMED "44054006"
Cả 3 cùng nói về Diabetes type 2. Nếu không chuẩn hoá, query "tổng số bệnh nhân Diabetes type 2" sẽ ra số sai.
OMOP giải quyết bằng cách chọn 1 Standard Concept (ở đây là concept_id = 201826, SNOMED 44054006) và map mọi source code về đó.
2. Concept — đơn vị cơ bản
Concept là 1 hàng trong bảng CONCEPT:
| concept_id | concept_name | domain_id | vocabulary_id | concept_class_id | standard_concept | concept_code |
|---|---|---|---|---|---|---|
| 201826 | Type 2 diabetes mellitus | Condition | SNOMED | Clinical Finding | S | 44054006 |
| 192279 | Diabetic complication | Condition | SNOMED | Clinical Finding | C | 74627003 |
| 45757466 | E11 (ICD-10-CM) | Condition | ICD10CM | ICD10CM code | (null = Source) | E11 |
standard_concept:
- S (Standard): dùng để phân tích
- C (Classification): để classify, không phải standard
- NULL (Source): code gốc, dùng để lưu source_value
3. Vocabulary và Domain

| Domain | Bảng OMOP | Standard Vocabulary chính |
|---|---|---|
| Condition | CONDITION_OCCURRENCE | SNOMED CT |
| Drug | DRUG_EXPOSURE | RxNorm + RxNorm Extension |
| Procedure | PROCEDURE_OCCURRENCE | SNOMED CT, CPT4, ICD-10-PCS |
| Measurement | MEASUREMENT | LOINC, SNOMED CT |
| Observation | OBSERVATION | SNOMED CT, LOINC |
| Device | DEVICE_EXPOSURE | SNOMED CT |
| Unit | (across) | UCUM |
| Race / Ethnicity | PERSON | OMOP Custom (cần map cho VN) |
4. Concept Relationship
Bảng CONCEPT_RELATIONSHIP lưu mối liên hệ giữa concept:
| concept_id_1 | concept_id_2 | relationship_id |
|---|---|---|
| 45757466 (ICD10 E11) | 201826 (SNOMED Diabetes T2) | "Maps to" |
| 201826 | 45757466 | "Mapped from" |
| 1503297 (Metformin) | 1503328 (Metformin 500mg tablet) | "Has form" |
Maps to cực quan trọng — đây là cách map source → standard:
-- Tìm Standard Concept tương ứng với ICD-10 E11
SELECT c2.concept_id, c2.concept_name
FROM concept c1
JOIN concept_relationship cr ON c1.concept_id = cr.concept_id_1
AND cr.relationship_id = 'Maps to'
JOIN concept c2 ON cr.concept_id_2 = c2.concept_id
WHERE c1.vocabulary_id = 'ICD10CM' AND c1.concept_code = 'E11';
5. Concept Ancestor — hierarchy
CONCEPT_ANCESTOR lưu hierarchy descendant:
-- Tìm tất cả con cháu của "Diabetes mellitus" (Type 1, Type 2, gestational, ...)
SELECT c.concept_id, c.concept_name, ca.min_levels_of_separation
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';
Cực mạnh khi định nghĩa cohort: "bệnh nhân tiểu đường bất kỳ loại" = chỉ cần ancestor 201820.
6. Athena — portal vocabulary

Quy trình:
- Tạo account miễn phí trên athena.ohdsi.org
- Chọn vocabulary cần (SNOMED, RxNorm, LOINC, ICD10CM, ICD10, ATC, ...)
- Một số vocabulary cần license (SNOMED CT cho VN: cần đăng ký SNOMED International affiliate license — miễn phí cho VN vì là low-income member)
- Download zip — chứa CONCEPT.csv, CONCEPT_RELATIONSHIP.csv, ...
- Import vào CDM database
7. Import vocabulary vào Postgres
-- Tạo schema và bảng theo CommonDataModel DDL
\i OMOPCDM_postgresql_5.4_ddl.sql
-- Import CSV (ví dụ)
COPY concept FROM '/path/CONCEPT.csv' DELIMITER E'\t' CSV HEADER QUOTE E'\b';
COPY concept_relationship FROM '/path/CONCEPT_RELATIONSHIP.csv' DELIMITER E'\t' CSV HEADER QUOTE E'\b';
COPY concept_ancestor FROM '/path/CONCEPT_ANCESTOR.csv' DELIMITER E'\t' CSV HEADER QUOTE E'\b';
-- ...
-- Tạo index
\i OMOPCDM_postgresql_5.4_indices.sql
-- Tạo PK
\i OMOPCDM_postgresql_5.4_primary_keys.sql
Sau khi import: ~6M concept (đầy đủ), ~12 GB. Có thể strip xuống bằng cách chỉ chọn vocabulary cần.
8. USAGI — code mapping tool
USAGI giúp map code source (ICD-10 VN, danh mục thuốc BYT) sang Standard Concept:

Workflow:
- File CSV input có cột
source_code, source_name - USAGI search bằng Lucene
- User review top match, chấp nhận hoặc sửa
- Export →
SOURCE_TO_CONCEPT_MAPtable
9. Vocabulary cho VN

9.1 Custom vocabulary cho VN
Khi không có standard tương ứng (vd dân tộc 54 VN), tạo Custom Vocabulary với:
vocabulary_id = 'VN_DANTOC'concept_idtừ 2 tỷ trở lên (range OHDSI dành cho custom)- Đăng ký vào bảng VOCABULARY
INSERT INTO vocabulary VALUES
('VN_DANTOC', 'Dân tộc Việt Nam (54)', 'http://...', '1.0', 2000000001);
INSERT INTO concept VALUES
(2000001001, 'Kinh', 'Race', 'VN_DANTOC', 'Race', 'S', '01', NULL, NULL);
-- ...
9.2 SNOMED CT VN
VN là Member của SNOMED International (đăng ký 2024 qua MOH). Affiliate license miễn phí với cá nhân/tổ chức trong nước. Đăng ký tại snomed.org.
10. Vocabulary upgrade
Athena release vocabulary monthly. Quy trình upgrade:

Lưu ý: concept_id stable giữa version, nhưng Maps to relationship có thể thay đổi → cần re-run ETL để cập nhật *_concept_id columns.
11. SQL pattern phổ biến
11.1 Lookup standard concept từ source code
SELECT c2.concept_id, c2.concept_name, c2.vocabulary_id
FROM concept c1
JOIN concept_relationship cr ON c1.concept_id = cr.concept_id_1
AND cr.relationship_id = 'Maps to'
JOIN concept c2 ON cr.concept_id_2 = c2.concept_id
WHERE c1.vocabulary_id = 'ICD10CM'
AND c1.concept_code = 'E11.9';
11.2 Tìm tất cả descendant của 1 ancestor
SELECT c.concept_id, c.concept_name
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';
11.3 RxNorm: tìm ingredient → tất cả product chứa nó
SELECT product.concept_id, product.concept_name
FROM concept ing
JOIN concept_ancestor ca ON ing.concept_id = ca.ancestor_concept_id
JOIN concept product ON ca.descendant_concept_id = product.concept_id
WHERE ing.concept_name = 'Metformin'
AND ing.concept_class_id = 'Ingredient'
AND product.concept_class_id IN ('Branded Drug', 'Clinical Drug');
12. Pitfall thường gặp
- ❌ Quên vocabulary upgrade → analytic dùng concept obsolete
- ❌ Map source→standard sai → cohort lệch hàng nghìn người
- ❌ Dùng concept Classification (C) thay vì Standard (S) trong analytic
- ❌ Quên hierarchy → cohort thiếu loại biến thể (vd "Diabetes" mà thiếu Type 1.5)
- ❌ Custom concept_id trùng với chuẩn (phải dùng 2-billion+ range)
- ❌ Không backup vocabulary trước upgrade
13. Workflow đề xuất cho dự án mới
- Đọc Themis convention OHDSI
- Tải vocabulary subset (SNOMED + ICD10CM + RxNorm + LOINC + ATC + UCUM) — đủ cho 90% VN
- Đăng ký SNOMED International affiliate (miễn phí VN)
- Import Postgres + index
- Setup USAGI cho mapping team
- Map source code lớn nhất trước (top 100 ICD-10 + top 200 thuốc = cover 80% volume)
- Lưu mapping vào
SOURCE_TO_CONCEPT_MAP+ commit Git - Schedule vocabulary upgrade hàng quý
Kết luận
Vocabulary là 50% giá trị của OMOP. Đầu tư đúng — bạn có thể join và phân tích bất kỳ dataset nào. Đầu tư sai — analytic của bạn sẽ luôn bị nghi ngờ. Bắt đầu nhỏ, review chặt, upgrade đều.
Bài tiếp: OMOP Core Clinical Tables — Person, Visit, Condition, Drug, Measurement, Observation.



