Database Schema - ระบบงาน Admission ผู้ป่วยใน (Inpatient Admission System)
Document Version: 1.0
Date: 28 สิงหาคม 2568
Based on: SRS Inpatient Admission System และ Master Database Schema
System Integration: EMR Core, OPD-CPOE, ER System, IPD-CPOE, DRG System
📋 Overview
ระบบ Admission ผู้ป่วยใน เป็นระบบหลักในการจัดการการรับผู้ป่วยเข้าโรงพยาบาล การจัดการเตียง และการประสานงานระหว่างระบบต่าง ๆ โดยเชื่อมต่อข้อมูลจากระบบ OPD, ER และส่งต่อข้อมูลไปยัง IPD-CPOE และ DRG System
🔗 System Dependencies
graph LR
A[EMR Core] --> E[Admission System]
B[OPD-CPOE] --> E
C[ER System] --> E
D[Appointment] --> E
E --> F[IPD-CPOE]
E --> G[DRG System]
E --> H[การเงินผู้ป่วยใน]
E --> I[พยาบาลผู้ป่วยใน]
🗃️ Core Admission Tables
1. Patient Admission Management
1.1 admissions - การรับผู้ป่วยใน
CREATE TABLE admissions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- Admission Identifiers (เชื่อมกับ Master Schema)
an VARCHAR(20) UNIQUE NOT NULL, -- Admission Number
hn VARCHAR(20) NOT NULL, -- Hospital Number
patient_id UUID NOT NULL REFERENCES patients(id),
visit_id UUID REFERENCES medical_visits(id),
-- Source Information
admission_source VARCHAR(50) NOT NULL CHECK (
admission_source IN ('OPD', 'ER', 'transfer_in', 'direct', 'appointment', 'readmission')
),
source_visit_id UUID, -- อ้างอิงไป OPD Visit หรือ ER Case
source_department_id UUID REFERENCES departments(id),
referring_doctor_id UUID REFERENCES users(id),
-- Admission Details
admission_date TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
admission_time TIME NOT NULL DEFAULT CURRENT_TIME,
admission_type VARCHAR(20) NOT NULL DEFAULT 'elective' CHECK (
admission_type IN ('emergency', 'elective', 'observation', 'same_day', 'newborn', 'maternity')
),
-- Medical Information
chief_complaint TEXT,
primary_diagnosis VARCHAR(200),
primary_icd10 VARCHAR(8), -- เชื่อมกับ ICD-10 codes
secondary_diagnoses JSONB DEFAULT '[]', -- Array of ICD-10 codes
provisional_diagnosis VARCHAR(200),
-- Physician Assignment
admitting_doctor_id UUID NOT NULL REFERENCES users(id),
attending_doctor_id UUID REFERENCES users(id),
consulting_doctors UUID[] DEFAULT '{}', -- Array of doctor IDs
-- Current Location
current_ward_id UUID REFERENCES wards(id),
current_bed_id UUID REFERENCES beds(id),
current_room VARCHAR(20),
-- Patient Classification
acuity_level INTEGER DEFAULT 1 CHECK (acuity_level BETWEEN 1 AND 5),
isolation_required BOOLEAN DEFAULT FALSE,
isolation_type VARCHAR(30), -- 'contact', 'droplet', 'airborne', 'protective'
special_needs TEXT,
-- Insurance & Financial
primary_insurance_type VARCHAR(50),
primary_insurance_number VARCHAR(100),
secondary_insurance_type VARCHAR(50),
secondary_insurance_number VARCHAR(100),
-- Admission Status
status VARCHAR(20) DEFAULT 'active' CHECK (
status IN ('pending', 'active', 'discharged', 'transferred_out', 'expired', 'cancelled')
),
-- Discharge Information
discharge_date TIMESTAMP WITH TIME ZONE,
discharge_time TIME,
discharge_type VARCHAR(30) CHECK (
discharge_type IN ('home', 'home_health', 'snf', 'rehab', 'ltac', 'hospice', 'ama', 'lama', 'transfer', 'expired')
),
discharge_diagnosis VARCHAR(200),
discharge_doctor_id UUID REFERENCES users(id),
-- Length of Stay
los_days INTEGER GENERATED ALWAYS AS (
CASE
WHEN discharge_date IS NOT NULL
THEN EXTRACT(days FROM (discharge_date - admission_date))::INTEGER
ELSE EXTRACT(days FROM (CURRENT_TIMESTAMP - admission_date))::INTEGER
END
) STORED,
-- Expected Discharge
expected_discharge_date DATE,
discharge_planning_notes TEXT,
-- Quality Indicators
readmission_within_30_days BOOLEAN DEFAULT FALSE,
previous_admission_id UUID REFERENCES admissions(id),
-- Special Flags
is_observation BOOLEAN DEFAULT FALSE,
is_same_day_surgery BOOLEAN DEFAULT FALSE,
is_newborn BOOLEAN DEFAULT FALSE,
is_emergency BOOLEAN DEFAULT FALSE,
-- Pre-admission Information
pre_admission_testing_complete BOOLEAN DEFAULT FALSE,
pre_admission_notes TEXT,
-- Audit Trail
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
created_by UUID REFERENCES users(id),
updated_by UUID REFERENCES users(id),
-- Constraints
CONSTRAINT valid_admission_dates CHECK (
discharge_date IS NULL OR discharge_date >= admission_date
),
CONSTRAINT valid_insurance CHECK (
primary_insurance_type IS NULL OR primary_insurance_number IS NOT NULL
)
);
-- Indexes for admissions
CREATE UNIQUE INDEX idx_admissions_an ON admissions(an);
CREATE INDEX idx_admissions_hn ON admissions(hn);
CREATE INDEX idx_admissions_patient_id ON admissions(patient_id);
CREATE INDEX idx_admissions_visit_id ON admissions(visit_id) WHERE visit_id IS NOT NULL;
CREATE INDEX idx_admissions_admission_date ON admissions(admission_date);
CREATE INDEX idx_admissions_source ON admissions(admission_source);
CREATE INDEX idx_admissions_status ON admissions(status);
CREATE INDEX idx_admissions_current_ward ON admissions(current_ward_id) WHERE current_ward_id IS NOT NULL;
CREATE INDEX idx_admissions_current_bed ON admissions(current_bed_id) WHERE current_bed_id IS NOT NULL;
CREATE INDEX idx_admissions_doctor ON admissions(admitting_doctor_id);
CREATE INDEX idx_admissions_los ON admissions(los_days);
CREATE INDEX idx_admissions_active ON admissions(status, admission_date)
WHERE status = 'active';
-- GIN index for JSON columns
CREATE INDEX idx_admissions_secondary_diagnoses ON admissions USING gin(secondary_diagnoses);
คำอธิบายฟิลด์ (Field Descriptions):
| Field | Type | Constraints | คำอธิบาย |
|---|---|---|---|
id |
UUID | PRIMARY KEY | รหัสเฉพาะของข้อมูลการรับผู้ป่วยใน |
an |
VARCHAR(20) | UNIQUE, NOT NULL | หมายเลขการรับผู้ป่วยใน (Admission Number) |
hn |
VARCHAR(20) | NOT NULL | หมายเลขผู้ป่วย (Hospital Number) |
patient_id |
UUID | NOT NULL, FK | รหัสผู้ป่วย (อ้างอิงจากตาราง patients) |
visit_id |
UUID | FK | รหัส Visit ที่เชื่อมโยงการมารับบริการ |
admission_source |
VARCHAR(50) | NOT NULL, CHECK | แหล่งที่มาของการรับผู้ป่วยใน (OPD, ER, transfer_in, direct, appointment, readmission) |
source_visit_id |
UUID | รหัส Visit ต้นทางจาก OPD หรือ ER | |
source_department_id |
UUID | FK | แผนกที่ส่งตัวมา |
referring_doctor_id |
UUID | FK | แพทย์ที่ส่งตัว |
admission_date |
TIMESTAMP | NOT NULL | วันเวลาที่รับผู้ป่วยเข้า |
admission_time |
TIME | NOT NULL | เวลาที่รับผู้ป่วยเข้า |
admission_type |
VARCHAR(20) | NOT NULL, CHECK | ประเภทการรับผู้ป่วยใน (emergency, elective, observation, same_day, newborn, maternity) |
chief_complaint |
TEXT | อาการสำคัญที่มาโรงพยาบาล | |
primary_diagnosis |
VARCHAR(200) | การวินิจฉัยหลัก | |
primary_icd10 |
VARCHAR(8) | รหัส ICD-10 ของการวินิจฉัยหลัก | |
secondary_diagnoses |
JSONB | การวินิจฉัยรอง (รายการรหัส ICD-10) | |
provisional_diagnosis |
VARCHAR(200) | การวินิจฉัยเบื้องต้น | |
admitting_doctor_id |
UUID | NOT NULL, FK | แพทย์ที่รับผู้ป่วยเข้า |
attending_doctor_id |
UUID | FK | แพทย์เจ้าของไข้ |
consulting_doctors |
UUID[] | รายการแพทย์ที่ปรึกษา | |
current_ward_id |
UUID | FK | หอผู้ป่วยปัจจุบัน |
current_bed_id |
UUID | FK | เตียงปัจจุบัน |
current_room |
VARCHAR(20) | ห้องปัจจุบัน | |
acuity_level |
INTEGER | CHECK | ระดับความรุนแรงของอาการ (1-5) |
isolation_required |
BOOLEAN | ต้องการแยกกักตัวหรือไม่ | |
isolation_type |
VARCHAR(30) | ประเภทการแยกกักตัว (contact, droplet, airborne, protective) | |
special_needs |
TEXT | ความต้องการพิเศษ | |
primary_insurance_type |
VARCHAR(50) | ประเภทสิทธิการรักษาหลัก | |
primary_insurance_number |
VARCHAR(100) | หมายเลขสิทธิการรักษาหลัก | |
secondary_insurance_type |
VARCHAR(50) | ประเภทสิทธิการรักษารอง | |
secondary_insurance_number |
VARCHAR(100) | หมายเลขสิทธิการรักษารอง | |
status |
VARCHAR(20) | CHECK | สถานะการรับผู้ป่วยใน (pending, active, discharged, transferred_out, expired, cancelled) |
discharge_date |
TIMESTAMP | วันเวลาที่จำหน่าย | |
discharge_time |
TIME | เวลาที่จำหน่าย | |
discharge_type |
VARCHAR(30) | CHECK | ประเภทการจำหน่าย (home, home_health, snf, rehab, ltac, hospice, ama, lama, transfer, expired) |
discharge_diagnosis |
VARCHAR(200) | การวินิจฉัยขณะจำหน่าย | |
discharge_doctor_id |
UUID | FK | แพทย์ที่จำหน่าย |
los_days |
INTEGER | GENERATED | จำนวนวันนอนโรงพยาบาล (คำนวณอัตโนมัติ) |
expected_discharge_date |
DATE | วันที่คาดว่าจะจำหน่าย | |
discharge_planning_notes |
TEXT | หมายเหตุการวางแผนจำหน่าย | |
readmission_within_30_days |
BOOLEAN | กลับมารักษาซ้ำภายใน 30 วันหรือไม่ | |
previous_admission_id |
UUID | FK | การรับผู้ป่วยครั้งก่อน (ถ้ามี) |
is_observation |
BOOLEAN | เป็นการรักษาแบบสังเกตอาการหรือไม่ | |
is_same_day_surgery |
BOOLEAN | เป็นผ่าตัดไปกลับในวันเดียวหรือไม่ | |
is_newborn |
BOOLEAN | เป็นทารกแรกเกิดหรือไม่ | |
is_emergency |
BOOLEAN | เป็นกรณีฉุกเฉินหรือไม่ | |
pre_admission_testing_complete |
BOOLEAN | ตรวจสอบก่อนรับผู้ป่วยเสร็จแล้วหรือไม่ | |
pre_admission_notes |
TEXT | หมายเหตุก่อนรับผู้ป่วย | |
created_at |
TIMESTAMP | DEFAULT NOW() | วันเวลาที่สร้างข้อมูล |
updated_at |
TIMESTAMP | DEFAULT NOW() | วันเวลาที่แก้ไขข้อมูลล่าสุด |
created_by |
UUID | FK | ผู้สร้างข้อมูล |
updated_by |
UUID | FK | ผู้แก้ไขข้อมูลล่าสุด |
1.2 admission_transfers - ประวัติการย้ายเตียง
CREATE TABLE admission_transfers (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
admission_id UUID NOT NULL REFERENCES admissions(id) ON DELETE CASCADE,
an VARCHAR(20) NOT NULL,
-- Transfer Details
transfer_type VARCHAR(30) NOT NULL CHECK (
transfer_type IN ('bed_change', 'room_change', 'ward_change', 'unit_change', 'level_of_care')
),
-- From Location
from_ward_id UUID REFERENCES wards(id),
from_bed_id UUID REFERENCES beds(id),
from_room VARCHAR(20),
from_unit VARCHAR(50),
-- To Location
to_ward_id UUID REFERENCES wards(id),
to_bed_id UUID REFERENCES beds(id),
to_room VARCHAR(20),
to_unit VARCHAR(50),
-- Transfer Information
transfer_date TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
transfer_reason VARCHAR(100),
transfer_reason_detail TEXT,
medical_necessity BOOLEAN DEFAULT FALSE,
-- Authorization
ordered_by UUID REFERENCES users(id),
approved_by UUID REFERENCES users(id),
executed_by UUID NOT NULL REFERENCES users(id),
-- Status
transfer_status VARCHAR(20) DEFAULT 'completed' CHECK (
transfer_status IN ('pending', 'in_progress', 'completed', 'cancelled')
),
-- Notes
transfer_notes TEXT,
patient_condition_notes TEXT,
-- Audit
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
created_by UUID REFERENCES users(id),
CONSTRAINT transfer_location_change CHECK (
(from_ward_id != to_ward_id) OR
(from_bed_id != to_bed_id) OR
(from_room != to_room)
)
);
-- Indexes for transfers
CREATE INDEX idx_admission_transfers_admission_id ON admission_transfers(admission_id);
CREATE INDEX idx_admission_transfers_an ON admission_transfers(an);
CREATE INDEX idx_admission_transfers_date ON admission_transfers(transfer_date);
CREATE INDEX idx_admission_transfers_from_bed ON admission_transfers(from_bed_id);
CREATE INDEX idx_admission_transfers_to_bed ON admission_transfers(to_bed_id);
CREATE INDEX idx_admission_transfers_type ON admission_transfers(transfer_type);
CREATE INDEX idx_admission_transfers_status ON admission_transfers(transfer_status);
คำอธิบายฟิลด์ (Field Descriptions):
| Field | Type | Constraints | คำอธิบาย |
|---|---|---|---|
id |
UUID | PRIMARY KEY | รหัสเฉพาะของการย้ายเตียง |
admission_id |
UUID | NOT NULL, FK | รหัสการรับผู้ป่วยใน |
an |
VARCHAR(20) | NOT NULL | หมายเลขการรับผู้ป่วยใน |
transfer_type |
VARCHAR(30) | NOT NULL, CHECK | ประเภทการย้าย (bed_change, room_change, ward_change, unit_change, level_of_care) |
from_ward_id |
UUID | FK | หอผู้ป่วยต้นทาง |
from_bed_id |
UUID | FK | เตียงต้นทาง |
from_room |
VARCHAR(20) | ห้องต้นทาง | |
from_unit |
VARCHAR(50) | หน่วยต้นทาง | |
to_ward_id |
UUID | FK | หอผู้ป่วยปลายทาง |
to_bed_id |
UUID | FK | เตียงปลายทาง |
to_room |
VARCHAR(20) | ห้องปลายทาง | |
to_unit |
VARCHAR(50) | หน่วยปลายทาง | |
transfer_date |
TIMESTAMP | NOT NULL | วันเวลาที่ย้าย |
transfer_reason |
VARCHAR(100) | เหตุผลการย้าย | |
transfer_reason_detail |
TEXT | รายละเอียดเหตุผลการย้าย | |
medical_necessity |
BOOLEAN | จำเป็นทางการแพทย์หรือไม่ | |
ordered_by |
UUID | FK | ผู้สั่งการย้าย |
approved_by |
UUID | FK | ผู้อนุมัติการย้าย |
executed_by |
UUID | NOT NULL, FK | ผู้ดำเนินการย้าย |
transfer_status |
VARCHAR(20) | CHECK | สถานะการย้าย (pending, in_progress, completed, cancelled) |
transfer_notes |
TEXT | หมายเหตุการย้าย | |
patient_condition_notes |
TEXT | หมายเหตุสภาพผู้ป่วย | |
created_at |
TIMESTAMP | DEFAULT NOW() | วันเวลาที่สร้างข้อมูล |
created_by |
UUID | FK | ผู้สร้างข้อมูล |
2. Bed and Ward Management
2.1 wards - หอผู้ป่วย/แผนก
CREATE TABLE wards (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
ward_code VARCHAR(20) UNIQUE NOT NULL,
ward_name VARCHAR(100) NOT NULL,
ward_name_en VARCHAR(100),
-- Ward Classification
ward_type VARCHAR(30) NOT NULL CHECK (
ward_type IN ('general', 'icu', 'ccu', 'nicu', 'picu', 'maternity', 'pediatric',
'surgical', 'medical', 'orthopedic', 'psychiatric', 'isolation', 'emergency')
),
specialty VARCHAR(50),
-- Physical Location
building VARCHAR(50),
floor_number INTEGER,
wing VARCHAR(20),
-- Capacity
total_beds INTEGER DEFAULT 0,
licensed_beds INTEGER DEFAULT 0,
staffed_beds INTEGER DEFAULT 0,
-- Ward Characteristics
is_icu BOOLEAN DEFAULT FALSE,
is_step_down BOOLEAN DEFAULT FALSE,
is_isolation_capable BOOLEAN DEFAULT FALSE,
is_maternity BOOLEAN DEFAULT FALSE,
is_pediatric BOOLEAN DEFAULT FALSE,
is_psychiatric BOOLEAN DEFAULT FALSE,
-- Staff Information
nurse_manager_id UUID REFERENCES users(id),
charge_nurse_id UUID REFERENCES users(id),
unit_clerk_id UUID REFERENCES users(id),
-- Operating Hours
visiting_hours_start TIME DEFAULT '08:00:00',
visiting_hours_end TIME DEFAULT '20:00:00',
restricted_visiting BOOLEAN DEFAULT FALSE,
-- Capabilities
oxygen_available BOOLEAN DEFAULT TRUE,
suction_available BOOLEAN DEFAULT TRUE,
telemetry_available BOOLEAN DEFAULT FALSE,
isolation_rooms INTEGER DEFAULT 0,
-- Contact Information
phone_number VARCHAR(20),
extension VARCHAR(10),
-- Status
is_active BOOLEAN DEFAULT TRUE,
temporarily_closed BOOLEAN DEFAULT FALSE,
closure_reason TEXT,
-- Audit
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
created_by UUID REFERENCES users(id),
updated_by UUID REFERENCES users(id)
);
-- Indexes for wards
CREATE UNIQUE INDEX idx_wards_code ON wards(ward_code);
CREATE INDEX idx_wards_name ON wards(ward_name);
CREATE INDEX idx_wards_type ON wards(ward_type);
CREATE INDEX idx_wards_building_floor ON wards(building, floor_number);
CREATE INDEX idx_wards_active ON wards(is_active);
CREATE INDEX idx_wards_specialty ON wards(specialty) WHERE specialty IS NOT NULL;
คำอธิบายฟิลด์ (Field Descriptions):
| Field | Type | Constraints | คำอธิบาย |
|---|---|---|---|
id |
UUID | PRIMARY KEY | รหัสเฉพาะของหอผู้ป่วย |
ward_code |
VARCHAR(20) | UNIQUE, NOT NULL | รหัสหอผู้ป่วย |
ward_name |
VARCHAR(100) | NOT NULL | ชื่อหอผู้ป่วย (ภาษาไทย) |
ward_name_en |
VARCHAR(100) | ชื่อหอผู้ป่วย (ภาษาอังกฤษ) | |
ward_type |
VARCHAR(30) | NOT NULL, CHECK | ประเภทหอผู้ป่วย (general, icu, ccu, nicu, picu, maternity, pediatric, surgical, medical, orthopedic, psychiatric, isolation, emergency) |
specialty |
VARCHAR(50) | ความเชี่ยวชาญ/สาขา | |
building |
VARCHAR(50) | อาคาร | |
floor_number |
INTEGER | ชั้น | |
wing |
VARCHAR(20) | ปีก/โซน | |
total_beds |
INTEGER | จำนวนเตียงทั้งหมด | |
licensed_beds |
INTEGER | จำนวนเตียงที่ได้รับใบอนุญาต | |
staffed_beds |
INTEGER | จำนวนเตียงที่มีพยาบาลดูแล | |
is_icu |
BOOLEAN | เป็นหอผู้ป่วยวิกฤตหรือไม่ | |
is_step_down |
BOOLEAN | เป็นหอผู้ป่วยกึ่งวิกฤตหรือไม่ | |
is_isolation_capable |
BOOLEAN | สามารถแยกกักตัวได้หรือไม่ | |
is_maternity |
BOOLEAN | เป็นหอคลอดหรือไม่ | |
is_pediatric |
BOOLEAN | เป็นหอเด็กหรือไม่ | |
is_psychiatric |
BOOLEAN | เป็นหอจิตเวชหรือไม่ | |
nurse_manager_id |
UUID | FK | รหัสหัวหน้าพยาบาล |
charge_nurse_id |
UUID | FK | รหัสพยาบาลหัวหน้าเวร |
unit_clerk_id |
UUID | FK | รหัสเสมียนหอผู้ป่วย |
visiting_hours_start |
TIME | เวลาเริ่มเยี่ยม | |
visiting_hours_end |
TIME | เวลาสิ้นสุดเยี่ยม | |
restricted_visiting |
BOOLEAN | จำกัดการเยี่ยมหรือไม่ | |
oxygen_available |
BOOLEAN | มีออกซิเจนหรือไม่ | |
suction_available |
BOOLEAN | มีเครื่องดูดเสมหะหรือไม่ | |
telemetry_available |
BOOLEAN | มีระบบตรวจสอบสัญญาณชีพหรือไม่ | |
isolation_rooms |
INTEGER | จำนวนห้องแยกกักตัว | |
phone_number |
VARCHAR(20) | เบอร์โทรศัพท์ | |
extension |
VARCHAR(10) | เบอร์ภายใน | |
is_active |
BOOLEAN | สถานะการใช้งาน | |
temporarily_closed |
BOOLEAN | ปิดชั่วคราวหรือไม่ | |
closure_reason |
TEXT | เหตุผลการปิด | |
created_at |
TIMESTAMP | DEFAULT NOW() | วันเวลาที่สร้างข้อมูล |
updated_at |
TIMESTAMP | DEFAULT NOW() | วันเวลาที่แก้ไขข้อมูลล่าสุด |
created_by |
UUID | FK | ผู้สร้างข้อมูล |
updated_by |
UUID | FK | ผู้แก้ไขข้อมูลล่าสุด |
2.2 beds - เตียงผู้ป่วย
CREATE TABLE beds (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
bed_code VARCHAR(20) UNIQUE NOT NULL,
bed_name VARCHAR(100) NOT NULL,
-- Location
ward_id UUID NOT NULL REFERENCES wards(id),
room_number VARCHAR(20),
bed_position VARCHAR(10), -- 'A', 'B', 'C', 'D' for multi-bed rooms
-- Bed Classification
bed_type VARCHAR(30) NOT NULL DEFAULT 'standard' CHECK (
bed_type IN ('standard', 'private', 'semi_private', 'icu', 'ccu', 'isolation',
'bariatric', 'pediatric', 'maternity', 'psychiatric', 'emergency')
),
bed_category VARCHAR(20) DEFAULT 'general' CHECK (
bed_category IN ('general', 'critical', 'intermediate', 'observation', 'specialty')
),
-- Current Status
status VARCHAR(20) DEFAULT 'available' CHECK (
status IN ('available', 'occupied', 'reserved', 'blocked', 'maintenance',
'housekeeping', 'out_of_order', 'isolation_pending')
),
-- Current Patient Information
current_patient_id UUID REFERENCES patients(id),
current_admission_id UUID REFERENCES admissions(id),
current_an VARCHAR(20),
occupied_since TIMESTAMP WITH TIME ZONE,
expected_discharge TIMESTAMP WITH TIME ZONE,
-- Physical Characteristics
is_private_room BOOLEAN DEFAULT FALSE,
has_bathroom BOOLEAN DEFAULT FALSE,
has_window BOOLEAN DEFAULT FALSE,
room_size_sqm DECIMAL(6,2),
-- Medical Equipment
has_oxygen BOOLEAN DEFAULT FALSE,
has_suction BOOLEAN DEFAULT FALSE,
has_compressed_air BOOLEAN DEFAULT FALSE,
has_cardiac_monitor BOOLEAN DEFAULT FALSE,
has_ventilator BOOLEAN DEFAULT FALSE,
has_dialysis_access BOOLEAN DEFAULT FALSE,
-- Special Features
is_isolation BOOLEAN DEFAULT FALSE,
isolation_type VARCHAR(30), -- 'negative_pressure', 'positive_pressure', 'contact', 'droplet'
is_bariatric BOOLEAN DEFAULT FALSE,
weight_limit_kg INTEGER DEFAULT 150,
-- Accessibility
is_wheelchair_accessible BOOLEAN DEFAULT TRUE,
has_hoyer_lift BOOLEAN DEFAULT FALSE,
-- Pricing Information
base_daily_rate DECIMAL(10,2) DEFAULT 0.00,
private_room_rate DECIMAL(10,2) DEFAULT 0.00,
icu_rate DECIMAL(10,2) DEFAULT 0.00,
-- Maintenance Information
last_maintenance_date DATE,
next_maintenance_date DATE,
maintenance_notes TEXT,
-- Housekeeping
last_cleaned TIMESTAMP WITH TIME ZONE,
cleaning_status VARCHAR(20) DEFAULT 'clean' CHECK (
cleaning_status IN ('clean', 'dirty', 'cleaning_in_progress', 'terminal_clean_required')
),
-- Reservation Information
reserved_for_patient_id UUID REFERENCES patients(id),
reserved_until TIMESTAMP WITH TIME ZONE,
reservation_notes TEXT,
-- Blocking Information
blocked_reason VARCHAR(100),
blocked_until TIMESTAMP WITH TIME ZONE,
blocked_by UUID REFERENCES users(id),
-- Status History
status_changed_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
status_changed_by UUID REFERENCES users(id),
-- Bed Management
is_temporary BOOLEAN DEFAULT FALSE,
is_emergency_bed BOOLEAN DEFAULT FALSE,
is_surge_capacity BOOLEAN DEFAULT FALSE,
-- Active Status
is_active BOOLEAN DEFAULT TRUE,
deactivated_date TIMESTAMP WITH TIME ZONE,
deactivation_reason TEXT,
-- Audit
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
created_by UUID REFERENCES users(id),
updated_by UUID REFERENCES users(id),
-- Constraints
CONSTRAINT bed_current_patient_consistent CHECK (
(current_patient_id IS NULL AND current_admission_id IS NULL AND current_an IS NULL) OR
(current_patient_id IS NOT NULL AND current_admission_id IS NOT NULL AND current_an IS NOT NULL)
),
CONSTRAINT bed_reservation_consistent CHECK (
(reserved_for_patient_id IS NULL AND reserved_until IS NULL) OR
(reserved_for_patient_id IS NOT NULL AND reserved_until IS NOT NULL AND reserved_until > CURRENT_TIMESTAMP)
)
);
-- Indexes for beds
CREATE UNIQUE INDEX idx_beds_code ON beds(bed_code);
CREATE INDEX idx_beds_ward_id ON beds(ward_id);
CREATE INDEX idx_beds_status ON beds(status);
CREATE INDEX idx_beds_type ON beds(bed_type);
CREATE INDEX idx_beds_available ON beds(status, ward_id) WHERE status = 'available';
CREATE INDEX idx_beds_occupied ON beds(status, current_patient_id) WHERE status = 'occupied';
CREATE INDEX idx_beds_room ON beds(ward_id, room_number);
CREATE INDEX idx_beds_current_patient ON beds(current_patient_id) WHERE current_patient_id IS NOT NULL;
CREATE INDEX idx_beds_current_admission ON beds(current_admission_id) WHERE current_admission_id IS NOT NULL;
CREATE INDEX idx_beds_reserved ON beds(reserved_for_patient_id, reserved_until)
WHERE reserved_for_patient_id IS NOT NULL;
CREATE INDEX idx_beds_active ON beds(is_active, ward_id);
CREATE INDEX idx_beds_pricing ON beds(bed_type, base_daily_rate);
คำอธิบายฟิลด์ (Field Descriptions):
| Field | Type | Constraints | คำอธิบาย |
|---|---|---|---|
id |
UUID | PRIMARY KEY | รหัสเฉพาะของเตียง |
bed_code |
VARCHAR(20) | UNIQUE, NOT NULL | รหัสเตียง |
bed_name |
VARCHAR(100) | NOT NULL | ชื่อเตียง |
ward_id |
UUID | NOT NULL, FK | รหัสหอผู้ป่วย |
room_number |
VARCHAR(20) | หมายเลขห้อง | |
bed_position |
VARCHAR(10) | ตำแหน่งเตียงในห้อง (A, B, C, D) | |
bed_type |
VARCHAR(30) | NOT NULL, CHECK | ประเภทเตียง (standard, private, semi_private, icu, ccu, isolation, bariatric, pediatric, maternity, psychiatric, emergency) |
bed_category |
VARCHAR(20) | CHECK | หมวดหมู่เตียง (general, critical, intermediate, observation, specialty) |
status |
VARCHAR(20) | CHECK | สถานะเตียง (available, occupied, reserved, blocked, maintenance, housekeeping, out_of_order, isolation_pending) |
current_patient_id |
UUID | FK | ผู้ป่วยปัจจุบัน |
current_admission_id |
UUID | FK | การรับผู้ป่วยปัจจุบัน |
current_an |
VARCHAR(20) | หมายเลข AN ปัจจุบัน | |
occupied_since |
TIMESTAMP | เวลาที่เริ่มใช้เตียง | |
expected_discharge |
TIMESTAMP | เวลาที่คาดว่าจะจำหน่าย | |
is_private_room |
BOOLEAN | เป็นห้องส่วนตัวหรือไม่ | |
has_bathroom |
BOOLEAN | มีห้องน้ำในตัวหรือไม่ | |
has_window |
BOOLEAN | มีหน้าต่างหรือไม่ | |
room_size_sqm |
DECIMAL(6,2) | ขนาดห้องตารางเมตร | |
has_oxygen |
BOOLEAN | มีออกซิเจนหรือไม่ | |
has_suction |
BOOLEAN | มีเครื่องดูดเสมหะหรือไม่ | |
has_compressed_air |
BOOLEAN | มีอากาศอัดหรือไม่ | |
has_cardiac_monitor |
BOOLEAN | มีเครื่องตรวจสอบหัวใจหรือไม่ | |
has_ventilator |
BOOLEAN | มีเครื่องช่วยหายใจหรือไม่ | |
has_dialysis_access |
BOOLEAN | มีช่องทางล้างไตหรือไม่ | |
is_isolation |
BOOLEAN | เป็นห้องแยกกักตัวหรือไม่ | |
isolation_type |
VARCHAR(30) | ประเภทการแยกกักตัว (negative_pressure, positive_pressure, contact, droplet) | |
is_bariatric |
BOOLEAN | เป็นเตียงสำหรับผู้ป่วยอ้วนหรือไม่ | |
weight_limit_kg |
INTEGER | ขีดจำกัดน้ำหนัก (กิโลกรัม) | |
is_wheelchair_accessible |
BOOLEAN | เข้าถึงได้ด้วยรถเข็นหรือไม่ | |
has_hoyer_lift |
BOOLEAN | มีเครื่องยกผู้ป่วยหรือไม่ | |
base_daily_rate |
DECIMAL(10,2) | อัตราค่าบริการต่อวัน (พื้นฐาน) | |
private_room_rate |
DECIMAL(10,2) | อัตราค่าบริการห้องส่วนตัว | |
icu_rate |
DECIMAL(10,2) | อัตราค่าบริการ ICU | |
last_maintenance_date |
DATE | วันที่ซ่อมบำรุงล่าสุด | |
next_maintenance_date |
DATE | วันที่ซ่อมบำรุงครั้งต่อไป | |
maintenance_notes |
TEXT | หมายเหตุการซ่อมบำรุง | |
last_cleaned |
TIMESTAMP | เวลาที่ทำความสะอาดล่าสุด | |
cleaning_status |
VARCHAR(20) | CHECK | สถานะความสะอาด (clean, dirty, cleaning_in_progress, terminal_clean_required) |
reserved_for_patient_id |
UUID | FK | ผู้ป่วยที่จองเตียงไว้ |
reserved_until |
TIMESTAMP | จองไว้จนถึงเวลา | |
reservation_notes |
TEXT | หมายเหตุการจอง | |
blocked_reason |
VARCHAR(100) | เหตุผลการปิดกั้น | |
blocked_until |
TIMESTAMP | ปิดกั้นจนถึงเวลา | |
blocked_by |
UUID | FK | ผู้ที่ปิดกั้น |
status_changed_at |
TIMESTAMP | เวลาที่เปลี่ยนสถานะ | |
status_changed_by |
UUID | FK | ผู้เปลี่ยนสถานะ |
is_temporary |
BOOLEAN | เป็นเตียงชั่วคราวหรือไม่ | |
is_emergency_bed |
BOOLEAN | เป็นเตียงฉุกเฉินหรือไม่ | |
is_surge_capacity |
BOOLEAN | เป็นเตียงเพิ่มกำลังผลิตหรือไม่ | |
is_active |
BOOLEAN | สถานะการใช้งาน | |
deactivated_date |
TIMESTAMP | วันที่ปิดการใช้งาน | |
deactivation_reason |
TEXT | เหตุผลการปิดใช้งาน | |
created_at |
TIMESTAMP | DEFAULT NOW() | วันเวลาที่สร้างข้อมูล |
updated_at |
TIMESTAMP | DEFAULT NOW() | วันเวลาที่แก้ไขข้อมูลล่าสุด |
created_by |
UUID | FK | ผู้สร้างข้อมูล |
updated_by |
UUID | FK | ผู้แก้ไขข้อมูลล่าสุด |
2.3 bed_reservations - การจองเตียง
CREATE TABLE bed_reservations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- Reservation Details
reservation_code VARCHAR(20) UNIQUE NOT NULL,
patient_id UUID NOT NULL REFERENCES patients(id),
hn VARCHAR(20) NOT NULL,
-- Bed Information
bed_id UUID REFERENCES beds(id),
ward_id UUID NOT NULL REFERENCES wards(id),
preferred_bed_type VARCHAR(30),
-- Timing
reservation_date TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
requested_admission_date TIMESTAMP WITH TIME ZONE NOT NULL,
requested_admission_time TIME,
expires_at TIMESTAMP WITH TIME ZONE NOT NULL,
-- Medical Information
diagnosis VARCHAR(200),
icd10_code VARCHAR(8),
expected_los INTEGER,
acuity_level INTEGER DEFAULT 1 CHECK (acuity_level BETWEEN 1 AND 5),
special_requirements TEXT,
-- Source Information
reservation_source VARCHAR(50) NOT NULL CHECK (
reservation_source IN ('appointment', 'opd', 'er', 'transfer', 'surgery', 'procedure', 'direct')
),
source_visit_id UUID,
source_appointment_id UUID,
-- Physician Information
requesting_doctor_id UUID NOT NULL REFERENCES users(id),
admitting_doctor_id UUID REFERENCES users(id),
-- Status
status VARCHAR(20) DEFAULT 'active' CHECK (
status IN ('active', 'confirmed', 'admitted', 'cancelled', 'expired', 'no_show')
),
-- Confirmation
confirmed_at TIMESTAMP WITH TIME ZONE,
confirmed_by UUID REFERENCES users(id),
confirmation_notes TEXT,
-- Cancellation
cancelled_at TIMESTAMP WITH TIME ZONE,
cancelled_by UUID REFERENCES users(id),
cancellation_reason VARCHAR(100),
cancellation_notes TEXT,
-- Admission Link
admission_id UUID REFERENCES admissions(id),
admitted_at TIMESTAMP WITH TIME ZONE,
-- Priority
priority INTEGER DEFAULT 3 CHECK (priority BETWEEN 1 AND 5), -- 1=highest, 5=lowest
is_urgent BOOLEAN DEFAULT FALSE,
-- Notification
patient_notified BOOLEAN DEFAULT FALSE,
notification_sent_at TIMESTAMP WITH TIME ZONE,
reminder_sent BOOLEAN DEFAULT FALSE,
-- Insurance Pre-approval
insurance_preauth_required BOOLEAN DEFAULT FALSE,
insurance_preauth_obtained BOOLEAN DEFAULT FALSE,
preauth_number VARCHAR(50),
-- Notes
reservation_notes TEXT,
patient_instructions TEXT,
-- Audit
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
created_by UUID REFERENCES users(id),
updated_by UUID REFERENCES users(id),
-- Constraints
CONSTRAINT valid_reservation_dates CHECK (
requested_admission_date >= CURRENT_DATE AND
expires_at > requested_admission_date
),
CONSTRAINT reservation_admitted_link CHECK (
(status = 'admitted' AND admission_id IS NOT NULL AND admitted_at IS NOT NULL) OR
(status != 'admitted')
)
);
-- Indexes for bed reservations
CREATE UNIQUE INDEX idx_bed_reservations_code ON bed_reservations(reservation_code);
CREATE INDEX idx_bed_reservations_patient ON bed_reservations(patient_id);
CREATE INDEX idx_bed_reservations_hn ON bed_reservations(hn);
CREATE INDEX idx_bed_reservations_bed ON bed_reservations(bed_id) WHERE bed_id IS NOT NULL;
CREATE INDEX idx_bed_reservations_ward ON bed_reservations(ward_id);
CREATE INDEX idx_bed_reservations_admission_date ON bed_reservations(requested_admission_date);
CREATE INDEX idx_bed_reservations_status ON bed_reservations(status);
CREATE INDEX idx_bed_reservations_active ON bed_reservations(status, requested_admission_date)
WHERE status IN ('active', 'confirmed');
CREATE INDEX idx_bed_reservations_expires ON bed_reservations(expires_at) WHERE status IN ('active', 'confirmed');
CREATE INDEX idx_bed_reservations_doctor ON bed_reservations(requesting_doctor_id);
CREATE INDEX idx_bed_reservations_source ON bed_reservations(reservation_source);
CREATE INDEX idx_bed_reservations_priority ON bed_reservations(priority, requested_admission_date);
คำอธิบายฟิลด์ (Field Descriptions):
| Field | Type | Constraints | คำอธิบาย |
|---|---|---|---|
id |
UUID | PRIMARY KEY | รหัสเฉพาะของการจองเตียง |
reservation_code |
VARCHAR(20) | UNIQUE, NOT NULL | รหัสการจองเตียง |
patient_id |
UUID | NOT NULL, FK | รหัสผู้ป่วยที่จองเตียง |
hn |
VARCHAR(20) | NOT NULL | หมายเลขผู้ป่วย |
bed_id |
UUID | FK | รหัสเตียงที่จองไว้ |
ward_id |
UUID | NOT NULL, FK | รหัสหอผู้ป่วยที่จองไว้ |
preferred_bed_type |
VARCHAR(30) | ประเภทเตียงที่ต้องการ | |
reservation_date |
TIMESTAMP | NOT NULL | วันเวลาที่จอง |
requested_admission_date |
TIMESTAMP | NOT NULL | วันเวลาที่ขอเข้ารับผู้ป่วย |
requested_admission_time |
TIME | เวลาที่ขอเข้ารับผู้ป่วย | |
expires_at |
TIMESTAMP | NOT NULL | วันหมดอายุการจอง |
diagnosis |
VARCHAR(200) | การวินิจฉัย | |
icd10_code |
VARCHAR(8) | รหัส ICD-10 | |
expected_los |
INTEGER | จำนวนวันที่คาดว่าจะนอนโรงพยาบาล | |
acuity_level |
INTEGER | CHECK | ระดับความรุนแรง (1-5) |
special_requirements |
TEXT | ความต้องการพิเศษ | |
reservation_source |
VARCHAR(50) | NOT NULL, CHECK | แหล่งที่มาการจอง (appointment, opd, er, transfer, surgery, procedure, direct) |
source_visit_id |
UUID | รหัส Visit ต้นทาง | |
source_appointment_id |
UUID | รหัสนัดหมายต้นทาง | |
requesting_doctor_id |
UUID | NOT NULL, FK | แพทย์ที่ขอจองเตียง |
admitting_doctor_id |
UUID | FK | แพทย์ที่จะรับผู้ป่วยเข้า |
status |
VARCHAR(20) | CHECK | สถานะการจอง (active, confirmed, admitted, cancelled, expired, no_show) |
confirmed_at |
TIMESTAMP | เวลาที่ยืนยันการจอง | |
confirmed_by |
UUID | FK | ผู้ยืนยันการจอง |
confirmation_notes |
TEXT | หมายเหตุการยืนยัน | |
cancelled_at |
TIMESTAMP | เวลาที่ยกเลิกการจอง | |
cancelled_by |
UUID | FK | ผู้ยกเลิกการจอง |
cancellation_reason |
VARCHAR(100) | เหตุผลการยกเลิก | |
cancellation_notes |
TEXT | หมายเหตุการยกเลิก | |
admission_id |
UUID | FK | รหัสการรับผู้ป่วยจริง |
admitted_at |
TIMESTAMP | เวลาที่รับผู้ป่วยจริง | |
priority |
INTEGER | CHECK | ลำดับความสำคัญ (1=สูงสุด, 5=ต่ำสุด) |
is_urgent |
BOOLEAN | เป็นกรณีด่วนหรือไม่ | |
patient_notified |
BOOLEAN | แจ้งผู้ป่วยแล้วหรือไม่ | |
notification_sent_at |
TIMESTAMP | เวลาที่ส่งการแจ้งเตือน | |
reminder_sent |
BOOLEAN | ส่งการแจ้งเตือนเกิดแล้วหรือไม่ | |
insurance_preauth_required |
BOOLEAN | ต้องการการอนุมัติสิทธิ์ล่วงหน้าหรือไม่ | |
insurance_preauth_obtained |
BOOLEAN | ได้รับการอนุมัติสิทธิ์แล้วหรือไม่ | |
preauth_number |
VARCHAR(50) | หมายเลขอนุมัติล่วงหน้า | |
reservation_notes |
TEXT | หมายเหตุการจองเตียง | |
patient_instructions |
TEXT | คำแนะนำสำหรับผู้ป่วย | |
created_at |
TIMESTAMP | DEFAULT NOW() | วันเวลาที่สร้างข้อมูล |
updated_at |
TIMESTAMP | DEFAULT NOW() | วันเวลาที่แก้ไขข้อมูลล่าสุด |
created_by |
UUID | FK | ผู้สร้างข้อมูล |
updated_by |
UUID | FK | ผู้แก้ไขข้อมูลล่าสุด |
3. Census and Occupancy Tracking
3.1 daily_census - สถิติผู้ป่วยใน รายวัน
CREATE TABLE daily_census (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
census_date DATE NOT NULL,
ward_id UUID NOT NULL REFERENCES wards(id),
-- Bed Counts
total_beds INTEGER DEFAULT 0,
staffed_beds INTEGER DEFAULT 0,
available_beds INTEGER DEFAULT 0,
occupied_beds INTEGER DEFAULT 0,
reserved_beds INTEGER DEFAULT 0,
blocked_beds INTEGER DEFAULT 0,
-- Patient Counts
total_patients INTEGER DEFAULT 0,
new_admissions INTEGER DEFAULT 0,
discharges INTEGER DEFAULT 0,
transfers_in INTEGER DEFAULT 0,
transfers_out INTEGER DEFAULT 0,
-- Occupancy Metrics
occupancy_rate DECIMAL(5,2) DEFAULT 0.00, -- Percentage
avg_los DECIMAL(5,2) DEFAULT 0.00,
bed_turnover_rate DECIMAL(5,2) DEFAULT 0.00,
-- Patient Days
patient_days INTEGER DEFAULT 0,
-- Acuity Levels
acuity_level_1 INTEGER DEFAULT 0,
acuity_level_2 INTEGER DEFAULT 0,
acuity_level_3 INTEGER DEFAULT 0,
acuity_level_4 INTEGER DEFAULT 0,
acuity_level_5 INTEGER DEFAULT 0,
-- Special Populations
isolation_patients INTEGER DEFAULT 0,
pediatric_patients INTEGER DEFAULT 0,
observation_patients INTEGER DEFAULT 0,
-- Midnight Census (standard reporting time)
midnight_census INTEGER DEFAULT 0,
-- Revenue
daily_revenue DECIMAL(12,2) DEFAULT 0.00,
-- Quality Indicators
readmissions_30_day INTEGER DEFAULT 0,
average_satisfaction_score DECIMAL(3,2),
-- Calculated Fields
calculated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
-- Audit
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT unique_daily_census UNIQUE (census_date, ward_id)
);
-- Indexes for daily census
CREATE INDEX idx_daily_census_date ON daily_census(census_date);
CREATE INDEX idx_daily_census_ward ON daily_census(ward_id);
CREATE INDEX idx_daily_census_occupancy ON daily_census(occupancy_rate);
CREATE INDEX idx_daily_census_date_ward ON daily_census(census_date, ward_id);
คำอธิบายฟิลด์ (Field Descriptions):
| Field | Type | Constraints | คำอธิบาย |
|---|---|---|---|
id |
UUID | PRIMARY KEY | รหัสเฉพาะของสถิติรายวัน |
census_date |
DATE | NOT NULL | วันที่ทำสถิติ |
ward_id |
UUID | NOT NULL, FK | รหัสหอผู้ป่วย |
total_beds |
INTEGER | จำนวนเตียงทั้งหมด | |
staffed_beds |
INTEGER | จำนวนเตียงที่มีพยาบาลดูแล | |
available_beds |
INTEGER | จำนวนเตียงว่าง | |
occupied_beds |
INTEGER | จำนวนเตียงที่มีผู้ป่วยใช้ | |
reserved_beds |
INTEGER | จำนวนเตียงที่จองไว้ | |
blocked_beds |
INTEGER | จำนวนเตียงที่ถูกปิดกั้น | |
total_patients |
INTEGER | จำนวนผู้ป่วยทั้งหมด | |
new_admissions |
INTEGER | จำนวนผู้ป่วยใหม่ | |
discharges |
INTEGER | จำนวนผู้ป่วยที่จำหน่าย | |
transfers_in |
INTEGER | จำนวนผู้ป่วยที่ย้ายเข้ามา | |
transfers_out |
INTEGER | จำนวนผู้ป่วยที่ย้ายออกไป | |
occupancy_rate |
DECIMAL(5,2) | อัตราการครองเตียง (เปอร์เซ็นต์) | |
avg_los |
DECIMAL(5,2) | จำนวันนอนโรงพยาบาลเฉลี่ย | |
bed_turnover_rate |
DECIMAL(5,2) | อัตราการหมุนเวียนของเตียง | |
patient_days |
INTEGER | จำนวันผู้ป่วย (Patient Days) | |
acuity_level_1 |
INTEGER | จำนวนผู้ป่วยระดับความรุนแรง 1 | |
acuity_level_2 |
INTEGER | จำนวนผู้ป่วยระดับความรุนแรง 2 | |
acuity_level_3 |
INTEGER | จำนวนผู้ป่วยระดับความรุนแรง 3 | |
acuity_level_4 |
INTEGER | จำนวนผู้ป่วยระดับความรุนแรง 4 | |
acuity_level_5 |
INTEGER | จำนวนผู้ป่วยระดับความรุนแรง 5 | |
isolation_patients |
INTEGER | จำนวนผู้ป่วยที่ต้องแยกกักตัว | |
pediatric_patients |
INTEGER | จำนวนผู้ป่วยเด็ก | |
observation_patients |
INTEGER | จำนวนผู้ป่วยสังเกตอาการ | |
midnight_census |
INTEGER | สถิติ Midnight Census (เที่ยงคืน) | |
daily_revenue |
DECIMAL(12,2) | รายได้รายวัน | |
readmissions_30_day |
INTEGER | จำนวนการกลับมารักษาซ้ำใน 30 วัน | |
average_satisfaction_score |
DECIMAL(3,2) | คะแนนความพึงพอใจเฉลี่ย | |
calculated_at |
TIMESTAMP | เวลาที่คำนวณข้อมูล | |
created_at |
TIMESTAMP | DEFAULT NOW() | วันเวลาที่สร้างข้อมูล |
updated_at |
TIMESTAMP | DEFAULT NOW() | วันเวลาที่แก้ไขข้อมูลล่าสุด |
4. Emergency and Special Situations
4.1 emergency_bed_allocations - การจัดสรรเตียงฉุกเฉิน
CREATE TABLE emergency_bed_allocations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- Emergency Details
emergency_type VARCHAR(50) NOT NULL CHECK (
emergency_type IN ('mass_casualty', 'disaster', 'pandemic', 'surge_capacity', 'critical_shortage')
),
emergency_level INTEGER NOT NULL CHECK (emergency_level BETWEEN 1 AND 5),
-- Activation
activated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
activated_by UUID NOT NULL REFERENCES users(id),
deactivated_at TIMESTAMP WITH TIME ZONE,
deactivated_by UUID REFERENCES users(id),
-- Affected Areas
affected_wards UUID[] DEFAULT '{}',
-- Bed Allocations
emergency_beds_created INTEGER DEFAULT 0,
regular_beds_converted INTEGER DEFAULT 0,
total_additional_capacity INTEGER DEFAULT 0,
-- Status
is_active BOOLEAN DEFAULT TRUE,
-- Documentation
activation_reason TEXT NOT NULL,
deactivation_reason TEXT,
emergency_notes TEXT,
-- Approval
approved_by UUID REFERENCES users(id),
approval_notes TEXT,
-- Audit
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
created_by UUID REFERENCES users(id),
updated_by UUID REFERENCES users(id)
);
-- Indexes for emergency allocations
CREATE INDEX idx_emergency_allocations_type ON emergency_bed_allocations(emergency_type);
CREATE INDEX idx_emergency_allocations_active ON emergency_bed_allocations(is_active, activated_at);
CREATE INDEX idx_emergency_allocations_level ON emergency_bed_allocations(emergency_level);
คำอธิบายฟิลด์ (Field Descriptions):
| Field | Type | Constraints | คำอธิบาย |
|---|---|---|---|
id |
UUID | PRIMARY KEY | รหัสเฉพาะของการจัดสรรเตียงฉุกเฉิน |
emergency_type |
VARCHAR(50) | NOT NULL, CHECK | ประเภทเหตุฉุกเฉิน (mass_casualty, disaster, pandemic, surge_capacity, critical_shortage) |
emergency_level |
INTEGER | NOT NULL, CHECK | ระดับความรุนแรงของเหตุฉุกเฉิน (1-5) |
activated_at |
TIMESTAMP | NOT NULL | เวลาที่เริ่มใช้งาน |
activated_by |
UUID | NOT NULL, FK | ผู้เริ่มใช้งาน |
deactivated_at |
TIMESTAMP | เวลาที่หยุดการใช้งาน | |
deactivated_by |
UUID | FK | ผู้หยุดการใช้งาน |
affected_wards |
UUID[] | รายการหอผู้ป่วยที่ได้รับผลกระทบ | |
emergency_beds_created |
INTEGER | จำนวนเตียงฉุกเฉินที่สร้างขึ้น | |
regular_beds_converted |
INTEGER | จำนวนเตียงปกติที่แปลงเป็นเตียงฉุกเฉิน | |
total_additional_capacity |
INTEGER | กำลังผลิตเพิ่มเติมรวม | |
is_active |
BOOLEAN | สถานะการทำงาน | |
activation_reason |
TEXT | NOT NULL | เหตุผลการเริ่มใช้งาน |
deactivation_reason |
TEXT | เหตุผลการหยุดการทำงาน | |
emergency_notes |
TEXT | หมายเหตุสถานการณ์ฉุกเฉิน | |
approved_by |
UUID | FK | ผู้อนุมัติ |
approval_notes |
TEXT | หมายเหตุการอนุมัติ | |
created_at |
TIMESTAMP | DEFAULT NOW() | วันเวลาที่สร้างข้อมูล |
updated_at |
TIMESTAMP | DEFAULT NOW() | วันเวลาที่แก้ไขข้อมูลล่าสุด |
created_by |
UUID | FK | ผู้สร้างข้อมูล |
updated_by |
UUID | FK | ผู้แก้ไขข้อมูลล่าสุด |
🔄 Integration Views and Procedures
1. Comprehensive Admission View
CREATE VIEW v_admissions_complete AS
SELECT
a.*,
p.first_name, p.last_name, p.national_id, p.date_of_birth,
w.ward_name, w.ward_type,
b.bed_code, b.bed_name, b.room_number,
admitting_doc.username as admitting_doctor_name,
attending_doc.username as attending_doctor_name,
CASE
WHEN a.status = 'active' THEN
EXTRACT(days FROM (CURRENT_TIMESTAMP - a.admission_date))::INTEGER
ELSE a.los_days
END as current_los
FROM admissions a
LEFT JOIN patients p ON a.patient_id = p.id
LEFT JOIN wards w ON a.current_ward_id = w.id
LEFT JOIN beds b ON a.current_bed_id = b.id
LEFT JOIN users admitting_doc ON a.admitting_doctor_id = admitting_doc.id
LEFT JOIN users attending_doc ON a.attending_doctor_id = attending_doc.id;
2. Real-time Bed Status View
CREATE VIEW v_bed_status_realtime AS
SELECT
b.*,
w.ward_name, w.ward_type, w.building, w.floor_number,
p.first_name, p.last_name, p.national_id,
a.admission_date, a.expected_discharge_date,
CASE
WHEN b.status = 'occupied' THEN
EXTRACT(days FROM (CURRENT_TIMESTAMP - a.admission_date))::INTEGER
ELSE NULL
END as patient_los,
br.reservation_code, br.requested_admission_date
FROM beds b
LEFT JOIN wards w ON b.ward_id = w.id
LEFT JOIN patients p ON b.current_patient_id = p.id
LEFT JOIN admissions a ON b.current_admission_id = a.id
LEFT JOIN bed_reservations br ON b.id = br.bed_id AND br.status IN ('active', 'confirmed')
WHERE b.is_active = true;
3. Ward Occupancy Summary View
CREATE VIEW v_ward_occupancy_summary AS
SELECT
w.id as ward_id,
w.ward_code,
w.ward_name,
w.ward_type,
w.total_beds,
COUNT(b.id) as actual_beds,
COUNT(CASE WHEN b.status = 'available' THEN 1 END) as available_beds,
COUNT(CASE WHEN b.status = 'occupied' THEN 1 END) as occupied_beds,
COUNT(CASE WHEN b.status = 'reserved' THEN 1 END) as reserved_beds,
COUNT(CASE WHEN b.status = 'blocked' THEN 1 END) as blocked_beds,
ROUND(
(COUNT(CASE WHEN b.status = 'occupied' THEN 1 END)::DECIMAL /
NULLIF(COUNT(CASE WHEN b.status IN ('available', 'occupied', 'reserved') THEN 1 END), 0)) * 100,
2
) as occupancy_percentage
FROM wards w
LEFT JOIN beds b ON w.id = b.ward_id AND b.is_active = true
WHERE w.is_active = true
GROUP BY w.id, w.ward_code, w.ward_name, w.ward_type, w.total_beds;
🔧 Stored Procedures and Functions
1. Admission Processing Functions
Auto-assign Bed Function
CREATE OR REPLACE FUNCTION auto_assign_bed(
p_patient_id UUID,
p_ward_id UUID DEFAULT NULL,
p_bed_type VARCHAR(30) DEFAULT 'standard',
p_special_requirements TEXT DEFAULT NULL
) RETURNS UUID AS $$
DECLARE
assigned_bed_id UUID;
available_bed RECORD;
BEGIN
-- Find best available bed
SELECT b.id INTO assigned_bed_id
FROM beds b
JOIN wards w ON b.ward_id = w.id
WHERE b.status = 'available'
AND b.is_active = true
AND (p_ward_id IS NULL OR b.ward_id = p_ward_id)
AND (p_bed_type IS NULL OR b.bed_type = p_bed_type)
AND b.blocked_until IS NULL OR b.blocked_until < CURRENT_TIMESTAMP
ORDER BY
CASE WHEN b.ward_id = p_ward_id THEN 1 ELSE 2 END,
CASE WHEN b.bed_type = p_bed_type THEN 1 ELSE 2 END,
b.bed_code
LIMIT 1;
IF assigned_bed_id IS NOT NULL THEN
-- Update bed status
UPDATE beds
SET status = 'occupied',
current_patient_id = p_patient_id,
occupied_since = CURRENT_TIMESTAMP,
status_changed_at = CURRENT_TIMESTAMP
WHERE id = assigned_bed_id;
END IF;
RETURN assigned_bed_id;
END;
$$ LANGUAGE plpgsql;
Calculate Ward Statistics
CREATE OR REPLACE FUNCTION calculate_ward_statistics(
p_ward_id UUID,
p_date DATE DEFAULT CURRENT_DATE
) RETURNS TABLE (
ward_id UUID,
census_date DATE,
total_beds INTEGER,
occupied_beds INTEGER,
occupancy_rate DECIMAL(5,2),
new_admissions INTEGER,
discharges INTEGER,
avg_los DECIMAL(5,2)
) AS $$
BEGIN
RETURN QUERY
SELECT
p_ward_id,
p_date,
COUNT(b.id)::INTEGER as total_beds,
COUNT(CASE WHEN b.status = 'occupied' THEN 1 END)::INTEGER as occupied_beds,
ROUND((COUNT(CASE WHEN b.status = 'occupied' THEN 1 END)::DECIMAL /
NULLIF(COUNT(b.id), 0)) * 100, 2) as occupancy_rate,
COUNT(CASE WHEN DATE(a.admission_date) = p_date THEN 1 END)::INTEGER as new_admissions,
COUNT(CASE WHEN DATE(a.discharge_date) = p_date THEN 1 END)::INTEGER as discharges,
ROUND(AVG(CASE WHEN a.status = 'discharged' THEN a.los_days END), 2) as avg_los
FROM beds b
LEFT JOIN admissions a ON b.current_admission_id = a.id
WHERE b.ward_id = p_ward_id
AND b.is_active = true;
END;
$$ LANGUAGE plpgsql;
2. Trigger Functions
Update Bed Status on Admission
CREATE OR REPLACE FUNCTION update_bed_status_on_admission()
RETURNS TRIGGER AS $$
BEGIN
-- Handle new admission
IF TG_OP = 'INSERT' THEN
-- Update bed status to occupied
IF NEW.current_bed_id IS NOT NULL THEN
UPDATE beds
SET status = 'occupied',
current_patient_id = NEW.patient_id,
current_admission_id = NEW.id,
current_an = NEW.an,
occupied_since = NEW.admission_date,
status_changed_at = CURRENT_TIMESTAMP
WHERE id = NEW.current_bed_id;
END IF;
RETURN NEW;
END IF;
-- Handle status changes
IF TG_OP = 'UPDATE' THEN
-- Discharge or transfer out
IF OLD.status = 'active' AND NEW.status IN ('discharged', 'transferred_out') THEN
UPDATE beds
SET status = 'available',
current_patient_id = NULL,
current_admission_id = NULL,
current_an = NULL,
occupied_since = NULL,
status_changed_at = CURRENT_TIMESTAMP
WHERE id = OLD.current_bed_id;
END IF;
-- Bed change
IF OLD.current_bed_id != NEW.current_bed_id THEN
-- Free old bed
IF OLD.current_bed_id IS NOT NULL THEN
UPDATE beds
SET status = 'available',
current_patient_id = NULL,
current_admission_id = NULL,
current_an = NULL,
occupied_since = NULL,
status_changed_at = CURRENT_TIMESTAMP
WHERE id = OLD.current_bed_id;
END IF;
-- Occupy new bed
IF NEW.current_bed_id IS NOT NULL THEN
UPDATE beds
SET status = 'occupied',
current_patient_id = NEW.patient_id,
current_admission_id = NEW.id,
current_an = NEW.an,
occupied_since = CURRENT_TIMESTAMP,
status_changed_at = CURRENT_TIMESTAMP
WHERE id = NEW.current_bed_id;
END IF;
END IF;
RETURN NEW;
END IF;
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trigger_update_bed_status_on_admission
AFTER INSERT OR UPDATE ON admissions
FOR EACH ROW
EXECUTE FUNCTION update_bed_status_on_admission();
📊 Analytics and Reporting Support
1. Materialized Views for Performance
Daily Census Materialized View
CREATE MATERIALIZED VIEW mv_daily_census_summary AS
SELECT
DATE(a.admission_date) as census_date,
w.id as ward_id,
w.ward_name,
w.ward_type,
COUNT(DISTINCT a.id) FILTER (WHERE a.status = 'active') as current_census,
COUNT(DISTINCT a.id) FILTER (WHERE DATE(a.admission_date) = DATE(a.admission_date)) as new_admissions,
COUNT(DISTINCT a.id) FILTER (WHERE DATE(a.discharge_date) = DATE(a.admission_date)) as discharges,
AVG(a.los_days) FILTER (WHERE a.status = 'discharged') as avg_los,
COUNT(DISTINCT b.id) as total_beds,
ROUND(
(COUNT(DISTINCT a.id) FILTER (WHERE a.status = 'active')::DECIMAL /
COUNT(DISTINCT b.id)) * 100, 2
) as occupancy_rate
FROM wards w
LEFT JOIN beds b ON w.id = b.ward_id AND b.is_active = true
LEFT JOIN admissions a ON b.current_admission_id = a.id
WHERE w.is_active = true
AND DATE(a.admission_date) >= CURRENT_DATE - INTERVAL '90 days'
GROUP BY DATE(a.admission_date), w.id, w.ward_name, w.ward_type
ORDER BY census_date DESC, w.ward_name;
-- Refresh daily at 1 AM
CREATE INDEX ON mv_daily_census_summary (census_date, ward_id);
2. Performance Indexes for Large Scale
Partitioning for Large Tables
-- Partition admissions table by admission_date
ALTER TABLE admissions RENAME TO admissions_old;
CREATE TABLE admissions (LIKE admissions_old INCLUDING ALL)
PARTITION BY RANGE (admission_date);
-- Create monthly partitions
CREATE TABLE admissions_y2024m08 PARTITION OF admissions
FOR VALUES FROM ('2024-08-01') TO ('2024-09-01');
CREATE TABLE admissions_y2024m09 PARTITION OF admissions
FOR VALUES FROM ('2024-09-01') TO ('2024-10-01');
-- Auto-create monthly partitions using pg_partman
SELECT partman.create_parent(
p_parent_table => 'public.admissions',
p_control => 'admission_date',
p_type => 'range',
p_interval => 'monthly'
);
🔐 Security and Access Control
Row Level Security (RLS)
Ward-based Access Control
-- Enable RLS on admissions
ALTER TABLE admissions ENABLE ROW LEVEL SECURITY;
-- Policy for ward-based access
CREATE POLICY admission_ward_access ON admissions
FOR ALL TO medical_staff
USING (
current_ward_id IN (
SELECT ward_id
FROM user_ward_assignments
WHERE user_id = current_setting('app.current_user_id')::UUID
)
);
-- Policy for admission staff (full access)
CREATE POLICY admission_staff_access ON admissions
FOR ALL TO admission_staff
USING (true);
-- Policy for bed management
ALTER TABLE beds ENABLE ROW LEVEL SECURITY;
CREATE POLICY bed_ward_access ON beds
FOR ALL TO medical_staff
USING (
ward_id IN (
SELECT ward_id
FROM user_ward_assignments
WHERE user_id = current_setting('app.current_user_id')::UUID
)
);
💾 Maintenance and Optimization
Automated Maintenance Tasks
-- Update daily census automatically
CREATE OR REPLACE FUNCTION update_daily_census()
RETURNS INTEGER AS $$
DECLARE
processed_wards INTEGER := 0;
ward_record RECORD;
BEGIN
FOR ward_record IN SELECT id FROM wards WHERE is_active = true
LOOP
INSERT INTO daily_census (census_date, ward_id, total_beds, occupied_beds, occupancy_rate, midnight_census)
SELECT
CURRENT_DATE,
ward_record.id,
COUNT(b.id),
COUNT(CASE WHEN b.status = 'occupied' THEN 1 END),
ROUND((COUNT(CASE WHEN b.status = 'occupied' THEN 1 END)::DECIMAL / COUNT(b.id)) * 100, 2),
COUNT(CASE WHEN b.status = 'occupied' THEN 1 END)
FROM beds b
WHERE b.ward_id = ward_record.id AND b.is_active = true
ON CONFLICT (census_date, ward_id)
DO UPDATE SET
total_beds = EXCLUDED.total_beds,
occupied_beds = EXCLUDED.occupied_beds,
occupancy_rate = EXCLUDED.occupancy_rate,
midnight_census = EXCLUDED.midnight_census,
updated_at = CURRENT_TIMESTAMP;
processed_wards := processed_wards + 1;
END LOOP;
RETURN processed_wards;
END;
$$ LANGUAGE plpgsql;
-- Schedule census update daily at midnight
SELECT cron.schedule('update-daily-census', '0 0 * * *', 'SELECT update_daily_census();');
📝 Notes
Important Implementation Considerations:
- Real-time Updates: ใช้ WebSocket สำหรับ bed status updates
- Performance: Optimized สำหรับ healthcare workloads
- Multi-source Integration: รองรับข้อมูลจาก OPD, ER, Appointment
- Bed Management: Advanced bed allocation algorithms
- Compliance: ตาม Healthcare standards และ Patient safety requirements
- Scalability: Partitioned tables สำหรับ large hospitals
Cross-System References:
- EMR Core Integration:
patients,medical_visits,users,departments - OPD-CPOE Integration:
visit_orders,visit_diagnoses - ER System Integration:
er_cases,er_triage - IPD-CPOE Integration:
ipd_orders,ipd_progress_notes - DRG System Integration:
drg_cases - Financial Integration:
billing_transactions,insurance_claims
ผู้จัดทำ: Claude Code - Senior Medical Technology Systems Analyst
วันที่: 28 สิงหาคม 2568
Version: 1.0