@xdev-asia/xdev-knowledge-mcp 1.0.56 → 1.0.58
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/content/blog/ai/minimax-danh-gia-chi-tiet-nen-tang-ai-full-stack-trung-quoc.md +450 -0
- package/content/metadata/authors/duy-tran.md +2 -0
- package/content/series/architecture/omop-cdm-5-4-cho-nguoi-moi-bat-dau/chapters/01-phan-1-tong-quan-nen-tang/lessons/bai-1-omop-cdm-la-gi-tai-sao-can-chuan-hoa-du-lieu-y-te.md +339 -0
- package/content/series/architecture/omop-cdm-5-4-cho-nguoi-moi-bat-dau/chapters/01-phan-1-tong-quan-nen-tang/lessons/bai-2-kien-truc-tong-the-omop-cdm-5-4-nhom-bang-nguyen-ly-thiet-ke.md +440 -0
- package/content/series/architecture/omop-cdm-5-4-cho-nguoi-moi-bat-dau/chapters/01-phan-1-tong-quan-nen-tang/lessons/bai-3-hieu-concept-trai-tim-cua-omop-cdm.md +441 -0
- package/content/series/architecture/omop-cdm-5-4-cho-nguoi-moi-bat-dau/chapters/02-phan-2-person-visit-nen-tang/lessons/bai-4-bang-person-quan-ly-danh-tinh-benh-nhan.md +323 -0
- package/content/series/architecture/omop-cdm-5-4-cho-nguoi-moi-bat-dau/chapters/02-phan-2-person-visit-nen-tang/lessons/bai-5-observation-period-khoang-thoi-gian-theo-doi-benh-nhan.md +308 -0
- package/content/series/architecture/omop-cdm-5-4-cho-nguoi-moi-bat-dau/chapters/02-phan-2-person-visit-nen-tang/lessons/bai-6-visit-occurrence-visit-detail-luot-kham-chi-tiet.md +353 -0
- package/content/series/architecture/omop-cdm-5-4-cho-nguoi-moi-bat-dau/chapters/03-phan-3-su-kien-lam-sang-chinh/lessons/bai-10-measurement-xet-nghiem-do-luong.md +409 -0
- package/content/series/architecture/omop-cdm-5-4-cho-nguoi-moi-bat-dau/chapters/03-phan-3-su-kien-lam-sang-chinh/lessons/bai-7-condition-occurrence-chan-doan-benh-ly.md +285 -0
- package/content/series/architecture/omop-cdm-5-4-cho-nguoi-moi-bat-dau/chapters/03-phan-3-su-kien-lam-sang-chinh/lessons/bai-8-drug-exposure-thuoc-dieu-tri.md +354 -0
- package/content/series/architecture/omop-cdm-5-4-cho-nguoi-moi-bat-dau/chapters/03-phan-3-su-kien-lam-sang-chinh/lessons/bai-9-procedure-occurrence-thu-thuat-phau-thuat.md +291 -0
- package/content/series/architecture/omop-cdm-5-4-cho-nguoi-moi-bat-dau/chapters/04-phan-4-bang-lam-sang-mo-rong/lessons/bai-11-observation-su-kien-lam-sang-tong-hop.md +289 -0
- package/content/series/architecture/omop-cdm-5-4-cho-nguoi-moi-bat-dau/chapters/04-phan-4-bang-lam-sang-mo-rong/lessons/bai-12-device-exposure-specimen-note.md +313 -0
- package/content/series/architecture/omop-cdm-5-4-cho-nguoi-moi-bat-dau/chapters/04-phan-4-bang-lam-sang-mo-rong/lessons/bai-13-death-episode-episode-event.md +346 -0
- package/content/series/architecture/omop-cdm-5-4-cho-nguoi-moi-bat-dau/chapters/05-phan-5-standardized-vocabularies/lessons/bai-14-concept-vocabulary-nen-tang-tu-dien-chuan.md +298 -0
- package/content/series/architecture/omop-cdm-5-4-cho-nguoi-moi-bat-dau/chapters/05-phan-5-standardized-vocabularies/lessons/bai-15-concept-relationship-concept-ancestor.md +336 -0
- package/content/series/architecture/omop-cdm-5-4-cho-nguoi-moi-bat-dau/chapters/05-phan-5-standardized-vocabularies/lessons/bai-16-drug-strength-cac-bang-vocabulary-con-lai.md +295 -0
- package/content/series/architecture/omop-cdm-5-4-cho-nguoi-moi-bat-dau/chapters/06-phan-6-health-system-economics-derived/lessons/bai-17-location-care-site-provider.md +334 -0
- package/content/series/architecture/omop-cdm-5-4-cho-nguoi-moi-bat-dau/chapters/06-phan-6-health-system-economics-derived/lessons/bai-18-payer-plan-period-cost.md +343 -0
- package/content/series/architecture/omop-cdm-5-4-cho-nguoi-moi-bat-dau/chapters/06-phan-6-health-system-economics-derived/lessons/bai-19-condition-era-drug-era-dose-era.md +418 -0
- package/content/series/architecture/omop-cdm-5-4-cho-nguoi-moi-bat-dau/chapters/07-phan-7-metadata-cohort-tong-ket/lessons/bai-20-cdm-source-metadata-cohort-tong-ket.md +517 -0
- package/content/series/architecture/omop-cdm-5-4-cho-nguoi-moi-bat-dau/index.md +385 -0
- package/package.json +1 -1
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: 019f1a00-a104-7b01-e001-omopcdm54004
|
|
3
|
+
title: "Bài 4: Bảng PERSON — Quản lý danh tính bệnh nhân"
|
|
4
|
+
slug: bai-4-bang-person-quan-ly-danh-tinh-benh-nhan
|
|
5
|
+
description: >-
|
|
6
|
+
Cấu trúc bảng PERSON, các trường bắt buộc (person_id,
|
|
7
|
+
gender_concept_id, year_of_birth), demographic data, liên kết
|
|
8
|
+
với LOCATION và PROVIDER, ETL conventions cho dữ liệu Việt Nam.
|
|
9
|
+
duration_minutes: 60
|
|
10
|
+
is_free: true
|
|
11
|
+
video_url: null
|
|
12
|
+
sort_order: 4
|
|
13
|
+
section_title: "Phần 2: Person & Visit — Nền tảng dữ liệu"
|
|
14
|
+
course:
|
|
15
|
+
id: 019f1a00-a100-7b01-e001-omopcdm54001
|
|
16
|
+
title: "OMOP CDM 5.4 cho Người mới — Hiểu từ A đến Z"
|
|
17
|
+
slug: omop-cdm-5-4-cho-nguoi-moi-bat-dau
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 340" style="max-width: 100%; height: auto; border-radius: 12px; margin-bottom: 1.5rem;">
|
|
21
|
+
<defs>
|
|
22
|
+
<linearGradient id="bg-omop04" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
23
|
+
<stop offset="0%" style="stop-color:#0c1222"/>
|
|
24
|
+
<stop offset="100%" style="stop-color:#1e293b"/>
|
|
25
|
+
</linearGradient>
|
|
26
|
+
</defs>
|
|
27
|
+
<rect width="1200" height="340" rx="12" fill="url(#bg-omop04)"/>
|
|
28
|
+
<g>
|
|
29
|
+
<circle cx="659" cy="87" r="22" fill="#818cf8" opacity="0.12"/>
|
|
30
|
+
<circle cx="718" cy="106" r="29" fill="#818cf8" opacity="0.09"/>
|
|
31
|
+
<circle cx="777" cy="125" r="36" fill="#818cf8" opacity="0.06"/>
|
|
32
|
+
<circle cx="836" cy="144" r="13" fill="#818cf8" opacity="0.13"/>
|
|
33
|
+
<circle cx="895" cy="163" r="20" fill="#818cf8" opacity="0.1"/>
|
|
34
|
+
<line x1="600" y1="157" x2="1100" y2="237" stroke="#818cf8" stroke-width="0.5" opacity="0.1"/>
|
|
35
|
+
</g>
|
|
36
|
+
<rect x="60" y="50" width="4" height="60" rx="2" fill="#818cf8"/>
|
|
37
|
+
<rect x="80" y="50" width="121" height="28" rx="14" fill="#818cf8" opacity="0.15"/>
|
|
38
|
+
<text x="92" y="69" font-family="system-ui,-apple-system,sans-serif" font-size="13" font-weight="600" fill="#818cf8">🏗️ Kiến trúc — Bài 4</text>
|
|
39
|
+
<text x="60" y="140" font-family="system-ui,-apple-system,sans-serif" font-size="34" font-weight="700" fill="#f1f5f9">
|
|
40
|
+
<tspan x="60" dy="0">Bảng PERSON — Quản lý</tspan>
|
|
41
|
+
<tspan x="60" dy="42">danh tính bệnh nhân</tspan>
|
|
42
|
+
</text>
|
|
43
|
+
<text x="60" y="244" font-family="system-ui,-apple-system,sans-serif" font-size="15" fill="#94a3b8" opacity="0.8">OMOP CDM 5.4 cho Người mới — Hiểu từ A đến Z</text>
|
|
44
|
+
<text x="60" y="268" font-family="system-ui,-apple-system,sans-serif" font-size="13" fill="#64748b" opacity="0.6">Phần 2: Person & Visit — Nền tảng dữ liệu</text>
|
|
45
|
+
<text x="1140" y="320" font-family="system-ui,-apple-system,sans-serif" font-size="12" fill="#475569" text-anchor="end" opacity="0.4">xdev.asia</text>
|
|
46
|
+
</svg>
|
|
47
|
+
|
|
48
|
+

|
|
49
|
+
|
|
50
|
+
## Giới thiệu
|
|
51
|
+
|
|
52
|
+
**PERSON** là bảng trung tâm của toàn bộ OMOP CDM — mọi bảng clinical đều tham chiếu đến PERSON qua `person_id`. Đây là nơi lưu trữ thông tin nhân khẩu học (demographics) của bệnh nhân.
|
|
53
|
+
|
|
54
|
+
Mỗi dòng trong PERSON = **một bệnh nhân duy nhất** (unique person).
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## 1. Cấu trúc bảng PERSON
|
|
59
|
+
|
|
60
|
+
### 1.1. Danh sách cột đầy đủ
|
|
61
|
+
|
|
62
|
+
| Cột | Kiểu | Bắt buộc | Mô tả |
|
|
63
|
+
|-----|------|----------|-------|
|
|
64
|
+
| `person_id` | INTEGER | ✅ PK | ID duy nhất cho mỗi bệnh nhân |
|
|
65
|
+
| `gender_concept_id` | INTEGER | ✅ | Giới tính (Standard Concept) |
|
|
66
|
+
| `year_of_birth` | INTEGER | ✅ | Năm sinh |
|
|
67
|
+
| `month_of_birth` | INTEGER | | Tháng sinh |
|
|
68
|
+
| `day_of_birth` | INTEGER | | Ngày sinh |
|
|
69
|
+
| `birth_datetime` | DATETIME | | Ngày giờ sinh đầy đủ |
|
|
70
|
+
| `race_concept_id` | INTEGER | ✅ | Chủng tộc (Standard Concept) |
|
|
71
|
+
| `ethnicity_concept_id` | INTEGER | ✅ | Dân tộc (Standard Concept) |
|
|
72
|
+
| `location_id` | INTEGER | FK | Địa chỉ (tham chiếu LOCATION) |
|
|
73
|
+
| `provider_id` | INTEGER | FK | Bác sĩ chính (tham chiếu PROVIDER) |
|
|
74
|
+
| `care_site_id` | INTEGER | FK | Cơ sở y tế (tham chiếu CARE_SITE) |
|
|
75
|
+
| `person_source_value` | VARCHAR(50) | | Mã bệnh nhân gốc từ HIS |
|
|
76
|
+
| `gender_source_value` | VARCHAR(50) | | Giới tính gốc (VD: "Nu", "F") |
|
|
77
|
+
| `gender_source_concept_id` | INTEGER | | Concept ID giới tính gốc |
|
|
78
|
+
| `race_source_value` | VARCHAR(50) | | Chủng tộc gốc |
|
|
79
|
+
| `race_source_concept_id` | INTEGER | | Concept ID chủng tộc gốc |
|
|
80
|
+
| `ethnicity_source_value` | VARCHAR(50) | | Dân tộc gốc |
|
|
81
|
+
| `ethnicity_source_concept_id` | INTEGER | | Concept ID dân tộc gốc |
|
|
82
|
+
|
|
83
|
+
### 1.2. Entity-Relationship
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
|
|
87
|
+
│ LOCATION │←──────│ PERSON │──────→│ PROVIDER │
|
|
88
|
+
│ location_id │ │ person_id │ │ provider_id │
|
|
89
|
+
│ address_1 │ │ gender_* │ │ provider_name│
|
|
90
|
+
│ city │ │ birth_* │ │ specialty_* │
|
|
91
|
+
│ state │ │ race_* │ └──────────────┘
|
|
92
|
+
│ zip │ │ ethnicity_* │ ↑
|
|
93
|
+
│ country_* │ │ location_id │ │
|
|
94
|
+
└──────────────┘ │ provider_id │ ┌──────┴───────┐
|
|
95
|
+
│ care_site_id│──────→│ CARE_SITE │
|
|
96
|
+
└──────┬───────┘ │ care_site_id │
|
|
97
|
+
│ │ care_site_name│
|
|
98
|
+
┌───────────┼───────────┐ └──────────────┘
|
|
99
|
+
↓ ↓ ↓
|
|
100
|
+
VISIT_OCC. CONDITION DRUG_EXPOSURE
|
|
101
|
+
OBSERVATION MEASUREMENT ... (tất cả clinical)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## 2. Các trường quan trọng chi tiết
|
|
107
|
+
|
|
108
|
+
### 2.1. person_id
|
|
109
|
+
|
|
110
|
+
- **Kiểu**: INTEGER, Primary Key
|
|
111
|
+
- **Quy tắc**: Duy nhất, không thay đổi, không có ý nghĩa lâm sàng
|
|
112
|
+
- **Không phải** mã bệnh nhân gốc (mã BN gốc lưu ở `person_source_value`)
|
|
113
|
+
|
|
114
|
+
```sql
|
|
115
|
+
-- ĐÚNG: person_id là số tự tăng hoặc hash
|
|
116
|
+
person_id = 100001
|
|
117
|
+
person_source_value = 'BN-2024-00123' -- Mã gốc từ HIS
|
|
118
|
+
|
|
119
|
+
-- SAI: Không dùng mã gốc làm person_id
|
|
120
|
+
-- person_id = 'BN-2024-00123' ← SAI (phải là INTEGER)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### 2.2. gender_concept_id
|
|
124
|
+
|
|
125
|
+
| Concept ID | Concept Name | Mô tả |
|
|
126
|
+
|-----------|--------------|-------|
|
|
127
|
+
| 8507 | Male | Nam |
|
|
128
|
+
| 8532 | Female | Nữ |
|
|
129
|
+
| 8551 | UNKNOWN | Không rõ |
|
|
130
|
+
| 8521 | OTHER | Khác |
|
|
131
|
+
|
|
132
|
+
```sql
|
|
133
|
+
-- Ví dụ ETL cho dữ liệu Việt Nam
|
|
134
|
+
CASE
|
|
135
|
+
WHEN gioi_tinh IN ('Nam', 'M', '1') THEN 8507 -- Male
|
|
136
|
+
WHEN gioi_tinh IN ('Nữ', 'Nu', 'F', '2') THEN 8532 -- Female
|
|
137
|
+
ELSE 8551 -- UNKNOWN
|
|
138
|
+
END AS gender_concept_id,
|
|
139
|
+
gioi_tinh AS gender_source_value
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### 2.3. year_of_birth, month_of_birth, day_of_birth
|
|
143
|
+
|
|
144
|
+
- `year_of_birth`: **Bắt buộc** — nếu không có, không nạp bệnh nhân
|
|
145
|
+
- `month_of_birth`, `day_of_birth`: Tùy chọn — đặt NULL nếu không có
|
|
146
|
+
- `birth_datetime`: Tùy chọn — hữu ích cho nhi khoa (tính tuổi chính xác)
|
|
147
|
+
|
|
148
|
+
```sql
|
|
149
|
+
-- Ví dụ: BN sinh ngày 15/03/1980
|
|
150
|
+
year_of_birth = 1980
|
|
151
|
+
month_of_birth = 3
|
|
152
|
+
day_of_birth = 15
|
|
153
|
+
birth_datetime = '1980-03-15 00:00:00'
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### 2.4. race_concept_id và ethnicity_concept_id
|
|
157
|
+
|
|
158
|
+
Đây là 2 trường theo chuẩn US Census. Cho dữ liệu Việt Nam:
|
|
159
|
+
|
|
160
|
+
| Trường | Khuyến nghị cho VN |
|
|
161
|
+
|--------|-------------------|
|
|
162
|
+
| `race_concept_id` | 8515 (Asian) |
|
|
163
|
+
| `race_source_value` | "Kinh", "Tày", "Mường"... |
|
|
164
|
+
| `ethnicity_concept_id` | 0 (No matching concept) |
|
|
165
|
+
| `ethnicity_source_value` | Ghi dân tộc gốc nếu có |
|
|
166
|
+
|
|
167
|
+
> **Lưu ý:** `race` và `ethnicity` trong OMOP theo chuẩn Mỹ (OMB). Khi ETL dữ liệu VN, ta vẫn phải đặt giá trị (dùng 0 nếu không map được) nhưng giữ thông tin gốc ở `*_source_value`.
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## 3. Ví dụ thực tế
|
|
172
|
+
|
|
173
|
+
### 3.1. Bệnh nhân Việt Nam
|
|
174
|
+
|
|
175
|
+
```sql
|
|
176
|
+
INSERT INTO person VALUES (
|
|
177
|
+
100001, -- person_id
|
|
178
|
+
8532, -- gender_concept_id (Female)
|
|
179
|
+
1980, -- year_of_birth
|
|
180
|
+
3, -- month_of_birth
|
|
181
|
+
15, -- day_of_birth
|
|
182
|
+
'1980-03-15 00:00:00', -- birth_datetime
|
|
183
|
+
8515, -- race_concept_id (Asian)
|
|
184
|
+
0, -- ethnicity_concept_id (N/A)
|
|
185
|
+
1001, -- location_id (→ LOCATION table)
|
|
186
|
+
5001, -- provider_id (→ PROVIDER table)
|
|
187
|
+
2001, -- care_site_id (→ CARE_SITE table)
|
|
188
|
+
'BN-2024-00123', -- person_source_value
|
|
189
|
+
'Nữ', -- gender_source_value
|
|
190
|
+
0, -- gender_source_concept_id
|
|
191
|
+
'Kinh', -- race_source_value
|
|
192
|
+
0, -- race_source_concept_id
|
|
193
|
+
NULL, -- ethnicity_source_value
|
|
194
|
+
0 -- ethnicity_source_concept_id
|
|
195
|
+
);
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### 3.2. SQL truy vấn cơ bản
|
|
199
|
+
|
|
200
|
+
```sql
|
|
201
|
+
-- Đếm bệnh nhân theo giới tính
|
|
202
|
+
SELECT
|
|
203
|
+
c.concept_name AS gender,
|
|
204
|
+
COUNT(*) AS patient_count
|
|
205
|
+
FROM person p
|
|
206
|
+
JOIN concept c ON p.gender_concept_id = c.concept_id
|
|
207
|
+
GROUP BY c.concept_name;
|
|
208
|
+
|
|
209
|
+
-- Phân bố tuổi
|
|
210
|
+
SELECT
|
|
211
|
+
EXTRACT(YEAR FROM CURRENT_DATE) - year_of_birth AS age,
|
|
212
|
+
COUNT(*) AS count
|
|
213
|
+
FROM person
|
|
214
|
+
GROUP BY 1
|
|
215
|
+
ORDER BY 1;
|
|
216
|
+
|
|
217
|
+
-- Tìm bệnh nhân có dữ liệu gốc
|
|
218
|
+
SELECT
|
|
219
|
+
person_id,
|
|
220
|
+
person_source_value AS ma_bn_goc,
|
|
221
|
+
gender_source_value AS gioi_tinh_goc,
|
|
222
|
+
race_source_value AS dan_toc
|
|
223
|
+
FROM person
|
|
224
|
+
WHERE person_source_value IS NOT NULL
|
|
225
|
+
LIMIT 10;
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## 4. ETL Conventions
|
|
231
|
+
|
|
232
|
+
### 4.1. Quy tắc quan trọng
|
|
233
|
+
|
|
234
|
+
| Quy tắc | Chi tiết |
|
|
235
|
+
|---------|----------|
|
|
236
|
+
| **1 person = 1 record** | Không trùng lặp, cần deduplicate |
|
|
237
|
+
| **year_of_birth bắt buộc** | Bỏ qua BN nếu không có năm sinh |
|
|
238
|
+
| **gender_concept_id bắt buộc** | Đặt 8551 (UNKNOWN) nếu không rõ |
|
|
239
|
+
| **person_id không mang ý nghĩa** | Đừng dùng mã BN gốc hoặc CMND/CCCD |
|
|
240
|
+
| **Không lưu PII trực tiếp** | Không có cột tên, CMND, sdt trong PERSON |
|
|
241
|
+
|
|
242
|
+
### 4.2. De-identification (Khử danh)
|
|
243
|
+
|
|
244
|
+
OMOP CDM **không có cột tên, số CMND/CCCD, số điện thoại**. Đây là thiết kế có chủ đích:
|
|
245
|
+
|
|
246
|
+
```
|
|
247
|
+
HIS gốc (có PII): OMOP CDM (de-identified):
|
|
248
|
+
┌─────────────────────────┐ ┌──────────────────────────┐
|
|
249
|
+
│ ma_bn: BN-2024-00123 │ → │ person_id: 100001 │
|
|
250
|
+
│ ho_ten: Nguyễn Thị Lan │ → │ (không có cột tên!) │
|
|
251
|
+
│ cmnd: 079123456789 │ → │ (không có cột CMND!) │
|
|
252
|
+
│ sdt: 0901234567 │ → │ (không có cột SĐT!) │
|
|
253
|
+
│ ngay_sinh: 15/03/1980 │ → │ year_of_birth: 1980 │
|
|
254
|
+
│ gioi_tinh: Nữ │ → │ gender_concept_id: 8532 │
|
|
255
|
+
└─────────────────────────┘ └──────────────────────────┘
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
> **Lưu ý:** `person_source_value` có thể chứa mã BN gốc (để truy vết). Tùy tổ chức có thể hash hoặc mã hóa giá trị này.
|
|
259
|
+
|
|
260
|
+
### 4.3. Xử lý trùng lặp
|
|
261
|
+
|
|
262
|
+
Khi bệnh nhân có ≥2 mã ở nhiều hệ thống:
|
|
263
|
+
|
|
264
|
+
```
|
|
265
|
+
HIS BV Chợ Rẫy: BN-CR-001 ┐
|
|
266
|
+
HIS BV Bạch Mai: BN-BM-555 ├──→ person_id = 100001
|
|
267
|
+
BHXH: DN-7900123456789 ┘ (1 person duy nhất)
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
ETL cần thực hiện **Patient Matching** (khớp bệnh nhân) trước khi nạp vào PERSON.
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## 5. Mối quan hệ với các bảng khác
|
|
275
|
+
|
|
276
|
+
```sql
|
|
277
|
+
-- Tất cả dữ liệu lâm sàng của 1 bệnh nhân
|
|
278
|
+
SELECT 'Visits' AS data_type, COUNT(*) AS count
|
|
279
|
+
FROM visit_occurrence WHERE person_id = 100001
|
|
280
|
+
UNION ALL
|
|
281
|
+
SELECT 'Conditions', COUNT(*)
|
|
282
|
+
FROM condition_occurrence WHERE person_id = 100001
|
|
283
|
+
UNION ALL
|
|
284
|
+
SELECT 'Drugs', COUNT(*)
|
|
285
|
+
FROM drug_exposure WHERE person_id = 100001
|
|
286
|
+
UNION ALL
|
|
287
|
+
SELECT 'Measurements', COUNT(*)
|
|
288
|
+
FROM measurement WHERE person_id = 100001
|
|
289
|
+
UNION ALL
|
|
290
|
+
SELECT 'Observations', COUNT(*)
|
|
291
|
+
FROM observation WHERE person_id = 100001;
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
---
|
|
295
|
+
|
|
296
|
+
## 6. Các lỗi ETL thường gặp
|
|
297
|
+
|
|
298
|
+
| Lỗi | Hậu quả | Cách khắc phục |
|
|
299
|
+
|-----|---------|---------------|
|
|
300
|
+
| Trùng person_id | Ghi đè dữ liệu | Kiểm tra unique constraint |
|
|
301
|
+
| year_of_birth = NULL | Vi phạm NOT NULL | Bỏ qua hoặc impute |
|
|
302
|
+
| gender_concept_id sai | Phân tích sai giới tính | Mapping chính xác |
|
|
303
|
+
| Để PII vào source_value | Vi phạm de-identification | Hash hoặc loại bỏ |
|
|
304
|
+
| Không deduplicate | 1 BN thành nhiều person | Patient matching trước ETL |
|
|
305
|
+
|
|
306
|
+
---
|
|
307
|
+
|
|
308
|
+
## Tổng kết
|
|
309
|
+
|
|
310
|
+
1. **PERSON** là bảng trung tâm — mọi bảng clinical tham chiếu qua `person_id`
|
|
311
|
+
2. **Trường bắt buộc**: person_id, gender_concept_id, year_of_birth, race_concept_id, ethnicity_concept_id
|
|
312
|
+
3. **Không chứa PII** (tên, CMND, SĐT) — thiết kế de-identified
|
|
313
|
+
4. **Liên kết**: LOCATION (địa chỉ), CARE_SITE (cơ sở), PROVIDER (bác sĩ chính)
|
|
314
|
+
5. **ETL VN**: gender mapping, race = Asian (8515), ethnicity = 0
|
|
315
|
+
|
|
316
|
+
**Bài tiếp theo:** OBSERVATION_PERIOD — tại sao cần biết "khoảng thời gian theo dõi" và cách nó ảnh hưởng đến mọi phân tích.
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## Tài liệu tham khảo
|
|
321
|
+
|
|
322
|
+
- [OMOP CDM 5.4 — PERSON](https://ohdsi.github.io/CommonDataModel/cdm54.html#PERSON)
|
|
323
|
+
- [The Book of OHDSI — Chapter 4.1](https://ohdsi.github.io/TheBookOfOhdsi/CommonDataModel.html)
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: 019f1a00-a105-7b01-e001-omopcdm54005
|
|
3
|
+
title: "Bài 5: OBSERVATION_PERIOD — Khoảng thời gian theo dõi bệnh nhân"
|
|
4
|
+
slug: bai-5-observation-period-khoang-thoi-gian-theo-doi-benh-nhan
|
|
5
|
+
description: >-
|
|
6
|
+
Ý nghĩa của OBSERVATION_PERIOD, tại sao đây là bảng bắt buộc,
|
|
7
|
+
cách xác định start/end date từ dữ liệu nguồn, ảnh hưởng đến
|
|
8
|
+
tính toán incidence/prevalence, và các quy ước ETL.
|
|
9
|
+
duration_minutes: 45
|
|
10
|
+
is_free: true
|
|
11
|
+
video_url: null
|
|
12
|
+
sort_order: 5
|
|
13
|
+
section_title: "Phần 2: Person & Visit — Nền tảng dữ liệu"
|
|
14
|
+
course:
|
|
15
|
+
id: 019f1a00-a100-7b01-e001-omopcdm54001
|
|
16
|
+
title: "OMOP CDM 5.4 cho Người mới — Hiểu từ A đến Z"
|
|
17
|
+
slug: omop-cdm-5-4-cho-nguoi-moi-bat-dau
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 340" style="max-width: 100%; height: auto; border-radius: 12px; margin-bottom: 1.5rem;">
|
|
21
|
+
<defs>
|
|
22
|
+
<linearGradient id="bg-omop05" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
23
|
+
<stop offset="0%" style="stop-color:#0c1222"/>
|
|
24
|
+
<stop offset="100%" style="stop-color:#1e293b"/>
|
|
25
|
+
</linearGradient>
|
|
26
|
+
</defs>
|
|
27
|
+
<rect width="1200" height="340" rx="12" fill="url(#bg-omop05)"/>
|
|
28
|
+
<g>
|
|
29
|
+
<circle cx="659" cy="87" r="22" fill="#818cf8" opacity="0.12"/>
|
|
30
|
+
<circle cx="718" cy="106" r="29" fill="#818cf8" opacity="0.09"/>
|
|
31
|
+
<circle cx="777" cy="125" r="36" fill="#818cf8" opacity="0.06"/>
|
|
32
|
+
<circle cx="836" cy="144" r="13" fill="#818cf8" opacity="0.13"/>
|
|
33
|
+
<circle cx="895" cy="163" r="20" fill="#818cf8" opacity="0.1"/>
|
|
34
|
+
<line x1="600" y1="157" x2="1100" y2="237" stroke="#818cf8" stroke-width="0.5" opacity="0.1"/>
|
|
35
|
+
</g>
|
|
36
|
+
<rect x="60" y="50" width="4" height="60" rx="2" fill="#818cf8"/>
|
|
37
|
+
<rect x="80" y="50" width="121" height="28" rx="14" fill="#818cf8" opacity="0.15"/>
|
|
38
|
+
<text x="92" y="69" font-family="system-ui,-apple-system,sans-serif" font-size="13" font-weight="600" fill="#818cf8">🏗️ Kiến trúc — Bài 5</text>
|
|
39
|
+
<text x="60" y="140" font-family="system-ui,-apple-system,sans-serif" font-size="34" font-weight="700" fill="#f1f5f9">
|
|
40
|
+
<tspan x="60" dy="0">OBSERVATION_PERIOD — Khoảng</tspan>
|
|
41
|
+
<tspan x="60" dy="42">thời gian theo dõi bệnh nhân</tspan>
|
|
42
|
+
</text>
|
|
43
|
+
<text x="60" y="244" font-family="system-ui,-apple-system,sans-serif" font-size="15" fill="#94a3b8" opacity="0.8">OMOP CDM 5.4 cho Người mới — Hiểu từ A đến Z</text>
|
|
44
|
+
<text x="60" y="268" font-family="system-ui,-apple-system,sans-serif" font-size="13" fill="#64748b" opacity="0.6">Phần 2: Person & Visit — Nền tảng dữ liệu</text>
|
|
45
|
+
<text x="1140" y="320" font-family="system-ui,-apple-system,sans-serif" font-size="12" fill="#475569" text-anchor="end" opacity="0.4">xdev.asia</text>
|
|
46
|
+
</svg>
|
|
47
|
+
|
|
48
|
+
## Giới thiệu
|
|
49
|
+
|
|
50
|
+
**OBSERVATION_PERIOD** là bảng mà nhiều người mới hay bỏ qua — nhưng nó cực kỳ quan trọng. Bảng này trả lời câu hỏi: **"Từ khi nào đến khi nào chúng ta có dữ liệu về bệnh nhân này?"**
|
|
51
|
+
|
|
52
|
+
Nếu bệnh nhân không có OBSERVATION_PERIOD, ta không thể phân biệt: "bệnh nhân không bị bệnh" hay "bệnh nhân bị bệnh nhưng không đến khám (nên không có dữ liệu)".
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## 1. Tại sao cần OBSERVATION_PERIOD?
|
|
57
|
+
|
|
58
|
+
### 1.1. Vấn đề "absence vs missing"
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
Bệnh nhân Lan:
|
|
62
|
+
├── 2020-01-10: Khám → chẩn đoán Tiểu đường
|
|
63
|
+
├── 2020-06-15: Tái khám
|
|
64
|
+
├── 2021-01-20: Tái khám
|
|
65
|
+
├── (im lặng 2 năm)
|
|
66
|
+
└── 2023-03-10: Nhập viện → Suy tim
|
|
67
|
+
|
|
68
|
+
Câu hỏi: Từ 2021-01 đến 2023-03, Lan có khỏe mạnh
|
|
69
|
+
hay chuyển sang bệnh viện khác?
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**OBSERVATION_PERIOD** cho biết khoảng thời gian bệnh nhân "nằm trong tầm quan sát" của nguồn dữ liệu:
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
Observation Period:
|
|
76
|
+
┌──────────────────────────────────────────────────────────────┐
|
|
77
|
+
│ 2020-01-10 ════════════════════════ 2021-12-31 │
|
|
78
|
+
│ (Có BHYT tại BV này) │
|
|
79
|
+
└──────────────────────────────────────────────────────────────┘
|
|
80
|
+
|
|
81
|
+
┌──────────────────────────────────────────────────────────────┐
|
|
82
|
+
│ 2023-01-01 ════════════════════════ 2024-06-30 │
|
|
83
|
+
│ (Quay lại BV, có BHYT mới) │
|
|
84
|
+
└──────────────────────────────────────────────────────────────┘
|
|
85
|
+
|
|
86
|
+
→ Trong observation period: không có Condition = bệnh nhân KHÔNG bị
|
|
87
|
+
→ Ngoài observation period: không có Condition = KHÔNG BIẾT
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### 1.2. Ảnh hưởng đến phân tích
|
|
91
|
+
|
|
92
|
+
| Phân tích | Không có OP | Có OP |
|
|
93
|
+
|-----------|-------------|-------|
|
|
94
|
+
| **Incidence rate** | Sai (mẫu số không chính xác) | Đúng (biết person-time at risk) |
|
|
95
|
+
| **Prevalence** | Sai (đếm không đủ) | Đúng (biết tổng population) |
|
|
96
|
+
| **Survival analysis** | Không biết censoring time | Time-to-event chính xác |
|
|
97
|
+
| **Cohort entry** | Có thể chọn BN ngoài dữ liệu | Chỉ chọn BN trong OP |
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## 2. Cấu trúc bảng
|
|
102
|
+
|
|
103
|
+
| Cột | Kiểu | Bắt buộc | Mô tả |
|
|
104
|
+
|-----|------|----------|-------|
|
|
105
|
+
| `observation_period_id` | INTEGER | ✅ PK | ID duy nhất |
|
|
106
|
+
| `person_id` | INTEGER | ✅ FK | Tham chiếu PERSON |
|
|
107
|
+
| `observation_period_start_date` | DATE | ✅ | Ngày bắt đầu có dữ liệu |
|
|
108
|
+
| `observation_period_end_date` | DATE | ✅ | Ngày kết thúc có dữ liệu |
|
|
109
|
+
| `period_type_concept_id` | INTEGER | ✅ | Nguồn gốc xác định OP |
|
|
110
|
+
|
|
111
|
+
### 2.1. period_type_concept_id
|
|
112
|
+
|
|
113
|
+
| Concept ID | Concept Name | Mô tả |
|
|
114
|
+
|-----------|--------------|-------|
|
|
115
|
+
| 32817 | EHR | Xác định từ EHR records |
|
|
116
|
+
| 32810 | Claim | Xác định từ claims/BHYT |
|
|
117
|
+
| 44814724 | Period covering healthcare encounters | Từ encounters |
|
|
118
|
+
| 44814725 | Period inferred by algorithm | Thuật toán suy ra |
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## 3. Cách xác định Observation Period
|
|
123
|
+
|
|
124
|
+
### 3.1. Từ dữ liệu claims/bảo hiểm
|
|
125
|
+
|
|
126
|
+
```
|
|
127
|
+
BHXH cấp thẻ BHYT:
|
|
128
|
+
┌─────────────────────────────────────────┐
|
|
129
|
+
│ Mã thẻ: DN-123456 Hiệu lực: 01/2020 │
|
|
130
|
+
│ BV đăng ký: Chợ Rẫy Hết hạn: 12/2024 │
|
|
131
|
+
└─────────────────────────────────────────┘
|
|
132
|
+
|
|
133
|
+
→ observation_period_start_date = 2020-01-01
|
|
134
|
+
→ observation_period_end_date = 2024-12-31
|
|
135
|
+
→ period_type_concept_id = 32810 (Claim)
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### 3.2. Từ dữ liệu EHR
|
|
139
|
+
|
|
140
|
+
Khi không có thông tin bảo hiểm rõ ràng, tính từ **encounter/visit đầu tiên đến cuối cùng**:
|
|
141
|
+
|
|
142
|
+
```sql
|
|
143
|
+
-- Tính OP từ visits
|
|
144
|
+
SELECT
|
|
145
|
+
person_id,
|
|
146
|
+
MIN(visit_start_date) AS observation_period_start_date,
|
|
147
|
+
MAX(COALESCE(visit_end_date, visit_start_date))
|
|
148
|
+
AS observation_period_end_date,
|
|
149
|
+
32817 AS period_type_concept_id -- EHR
|
|
150
|
+
FROM visit_occurrence
|
|
151
|
+
GROUP BY person_id;
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### 3.3. Một bệnh nhân có thể có nhiều Observation Periods
|
|
155
|
+
|
|
156
|
+
```
|
|
157
|
+
Bệnh nhân person_id = 100001:
|
|
158
|
+
|
|
159
|
+
OP 1: ═══════════ (2018-01-01 → 2019-06-30)
|
|
160
|
+
Có BHYT tại BV A
|
|
161
|
+
|
|
162
|
+
Gap (6 tháng, không có dữ liệu)
|
|
163
|
+
|
|
164
|
+
OP 2: ═══════════════════ (2020-01-01 → 2024-12-31)
|
|
165
|
+
Có BHYT mới tại BV A
|
|
166
|
+
|
|
167
|
+
→ 2 records trong OBSERVATION_PERIOD
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
```sql
|
|
171
|
+
INSERT INTO observation_period VALUES
|
|
172
|
+
(1, 100001, '2018-01-01', '2019-06-30', 32810),
|
|
173
|
+
(2, 100001, '2020-01-01', '2024-12-31', 32810);
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## 4. Quy tắc quan trọng
|
|
179
|
+
|
|
180
|
+
### 4.1. Mọi clinical event phải nằm trong Observation Period
|
|
181
|
+
|
|
182
|
+
```
|
|
183
|
+
OP: ════════════════════════════════════
|
|
184
|
+
2020-01-01 2024-12-31
|
|
185
|
+
|
|
186
|
+
✅ Visit 2020-03-15 (trong OP)
|
|
187
|
+
✅ Condition 2022-06-10 (trong OP)
|
|
188
|
+
❌ Drug Exposure 2019-05-10 (NGOÀI OP!) → Cảnh báo data quality
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
**ACHILLES data quality checks**: kiểm tra xem có clinical events nào nằm ngoài OBSERVATION_PERIOD không.
|
|
192
|
+
|
|
193
|
+
### 4.2. Observation Periods không được chồng lấp
|
|
194
|
+
|
|
195
|
+
Cho cùng 1 person_id, các OP phải **thứ tự thời gian, không overlap**:
|
|
196
|
+
|
|
197
|
+
```
|
|
198
|
+
✅ ĐÚng:
|
|
199
|
+
OP1: ═══════ OP2: ═══════════
|
|
200
|
+
2018-01 2019-06 2020-01 2024-12
|
|
201
|
+
|
|
202
|
+
❌ SAI (overlap):
|
|
203
|
+
OP1: ═══════════════
|
|
204
|
+
OP2: ═══════════════
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### 4.3. Quy ước đặc biệt
|
|
208
|
+
|
|
209
|
+
| Tình huống | Xử lý |
|
|
210
|
+
|-----------|-------|
|
|
211
|
+
| BN chỉ đến 1 lần | start_date = end_date = ngày khám |
|
|
212
|
+
| BN tử vong | end_date = ngày tử vong |
|
|
213
|
+
| Gap < 32 ngày (Claim) | Thường gộp thành 1 OP |
|
|
214
|
+
| Nhiều nguồn overlap | Gộp thành OP lớn nhất |
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## 5. Ứng dụng trong phân tích
|
|
219
|
+
|
|
220
|
+
### 5.1. Tính Person-Time at Risk
|
|
221
|
+
|
|
222
|
+
```sql
|
|
223
|
+
-- Tổng thời gian theo dõi (person-years)
|
|
224
|
+
SELECT
|
|
225
|
+
SUM(
|
|
226
|
+
observation_period_end_date - observation_period_start_date
|
|
227
|
+
) / 365.25 AS total_person_years
|
|
228
|
+
FROM observation_period;
|
|
229
|
+
|
|
230
|
+
-- Person-time cho incidence rate
|
|
231
|
+
SELECT
|
|
232
|
+
p.gender_concept_id,
|
|
233
|
+
SUM(
|
|
234
|
+
op.observation_period_end_date - op.observation_period_start_date
|
|
235
|
+
) / 365.25 AS person_years
|
|
236
|
+
FROM observation_period op
|
|
237
|
+
JOIN person p ON op.person_id = p.person_id
|
|
238
|
+
GROUP BY p.gender_concept_id;
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### 5.2. Lọc bệnh nhân "đủ dữ liệu"
|
|
242
|
+
|
|
243
|
+
```sql
|
|
244
|
+
-- Chỉ chọn BN có ≥ 1 năm follow-up
|
|
245
|
+
SELECT person_id
|
|
246
|
+
FROM observation_period
|
|
247
|
+
WHERE observation_period_end_date - observation_period_start_date >= 365
|
|
248
|
+
GROUP BY person_id;
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### 5.3. Kiểm tra data quality
|
|
252
|
+
|
|
253
|
+
```sql
|
|
254
|
+
-- Tìm events nằm ngoài observation period
|
|
255
|
+
SELECT
|
|
256
|
+
'CONDITION' AS event_type,
|
|
257
|
+
co.person_id,
|
|
258
|
+
co.condition_start_date AS event_date
|
|
259
|
+
FROM condition_occurrence co
|
|
260
|
+
LEFT JOIN observation_period op
|
|
261
|
+
ON co.person_id = op.person_id
|
|
262
|
+
AND co.condition_start_date
|
|
263
|
+
BETWEEN op.observation_period_start_date
|
|
264
|
+
AND op.observation_period_end_date
|
|
265
|
+
WHERE op.observation_period_id IS NULL;
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
## 6. Ví dụ hoàn chỉnh
|
|
271
|
+
|
|
272
|
+
```sql
|
|
273
|
+
-- OBSERVATION_PERIOD cho bệnh viện VN
|
|
274
|
+
INSERT INTO observation_period (
|
|
275
|
+
observation_period_id,
|
|
276
|
+
person_id,
|
|
277
|
+
observation_period_start_date,
|
|
278
|
+
observation_period_end_date,
|
|
279
|
+
period_type_concept_id
|
|
280
|
+
) VALUES
|
|
281
|
+
-- BN 100001: có BHYT từ 2020 đến 2024
|
|
282
|
+
(1, 100001, '2020-01-01', '2024-12-31', 32810),
|
|
283
|
+
-- BN 100002: đến khám 3 lần trong 2023
|
|
284
|
+
(2, 100002, '2023-02-15', '2023-11-20', 32817),
|
|
285
|
+
-- BN 100003: 2 giai đoạn khác nhau
|
|
286
|
+
(3, 100003, '2019-03-10', '2020-06-30', 32817),
|
|
287
|
+
(4, 100003, '2022-01-15', '2024-06-30', 32817);
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
## Tổng kết
|
|
293
|
+
|
|
294
|
+
1. **OBSERVATION_PERIOD** = khoảng thời gian bệnh nhân "có dữ liệu" trong hệ thống
|
|
295
|
+
2. Phân biệt **"không bị bệnh"** vs **"không có dữ liệu"**
|
|
296
|
+
3. **Bắt buộc** cho mọi person — cần ít nhất 1 OP per person
|
|
297
|
+
4. **Không overlap** giữa các OP cùng person_id
|
|
298
|
+
5. **Mọi clinical events** phải nằm trong OP
|
|
299
|
+
6. **Ứng dụng chính**: tính person-time, incidence rate, prevalence, cohort definition
|
|
300
|
+
|
|
301
|
+
**Bài tiếp theo:** VISIT_OCCURRENCE & VISIT_DETAIL — cách OMOP CDM ghi nhận mỗi lần bệnh nhân tiếp xúc với hệ thống y tế.
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
## Tài liệu tham khảo
|
|
306
|
+
|
|
307
|
+
- [OMOP CDM 5.4 — OBSERVATION_PERIOD](https://ohdsi.github.io/CommonDataModel/cdm54.html#OBSERVATION_PERIOD)
|
|
308
|
+
- [The Book of OHDSI — Observation Periods](https://ohdsi.github.io/TheBookOfOhdsi/CommonDataModel.html)
|