ข้ามไปที่เนื้อหา

Database Schema - Laboratory Master Data

โครงสร้างตารางฐานข้อมูลสำหรับระบบ Master Data ห้องปฏิบัติการ

📊 ภาพรวม Tables

erDiagram
    lab_categories ||--o{ lab_items : "categorizes"
    specimen_types ||--o{ lab_items : "requires"
    container_types ||--o{ lab_items : "uses"
    lab_items ||--o{ lab_panel_items : "includes"
    lab_panels ||--o{ lab_panel_items : "contains"
    lab_items ||--o{ lab_normal_ranges : "has"
    lab_items ||--o{ lab_critical_values : "has"
    lab_items ||--o{ lab_item_coverage : "covered_by"
    eligibility_schemes ||--o{ lab_item_coverage : "provides"

1. lab_categories (หมวดหมู่รายการตรวจ)

จัดกลุ่มรายการตรวจตามประเภท เช่น โลหิตวิทยา, เคมีคลินิก, จุลชีววิทยา

Schema

CREATE TABLE lab_categories (
  id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  code VARCHAR(20) NOT NULL UNIQUE COMMENT 'รหัสหมวดหมู่ (HEMA, CHEM, MICRO)',
  name_th VARCHAR(255) NOT NULL COMMENT 'ชื่อภาษาไทย',
  name_en VARCHAR(255) NOT NULL COMMENT 'ชื่อภาษาอังกฤษ',
  color VARCHAR(20) DEFAULT NULL COMMENT 'สีแสดงผล (Hex Color Code)',
  display_order INT UNSIGNED DEFAULT 0 COMMENT 'ลำดับการแสดงผล',
  is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน (0=ไม่ใช้งาน, 1=ใช้งาน)',
  created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  created_by VARCHAR(50) DEFAULT NULL COMMENT 'ผู้สร้างข้อมูล (User ID)',
  updated_by VARCHAR(50) DEFAULT NULL COMMENT 'ผู้แก้ไขข้อมูลล่าสุด',

  INDEX idx_code (code),
  INDEX idx_active (is_active),
  INDEX idx_display_order (display_order)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='หมวดหมู่รายการตรวจ Lab';

Columns

Column Type Nullable Description
id BIGINT UNSIGNED NO Primary Key
code VARCHAR(20) NO รหัสหมวดหมู่ (HEMA, CHEM, MICRO) - UNIQUE
name_th VARCHAR(255) NO ชื่อภาษาไทย
name_en VARCHAR(255) NO ชื่อภาษาอังกฤษ
color VARCHAR(20) YES สีแสดงผล (Hex #RRGGBB)
display_order INT UNSIGNED NO ลำดับการแสดงผล (0=แรกสุด)
is_active TINYINT(1) NO สถานะการใช้งาน (1=ใช้งาน, 0=ไม่ใช้งาน)
created_at DATETIME NO วันเวลาที่สร้าง
updated_at DATETIME NO วันเวลาที่แก้ไขล่าสุด
created_by VARCHAR(50) YES ผู้สร้างข้อมูล
updated_by VARCHAR(50) YES ผู้แก้ไขข้อมูล

Business Rules

  • code ต้อง UNIQUE และเป็น uppercase
  • display_order ใช้สำหรับจัดเรียงใน UI (เรียงจากน้อยไปมาก)
  • color เก็บเป็น Hex color code (#0d9488) สำหรับแสดงใน UI
  • ไม่ควรลบข้อมูลจริง ให้ใช้ is_active = 0 แทน

2. specimen_types (ประเภทสิ่งส่งตรวจ)

ประเภทของสิ่งส่งตรวจที่รับตรวจ เช่น เลือด, ปัสสาวะ, อุจจาระ

Schema

CREATE TABLE specimen_types (
  id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  code VARCHAR(20) NOT NULL UNIQUE COMMENT 'รหัสประเภทสิ่งส่งตรวจ (BLOOD, URINE)',
  name_th VARCHAR(255) NOT NULL COMMENT 'ชื่อภาษาไทย',
  name_en VARCHAR(255) NOT NULL COMMENT 'ชื่อภาษาอังกฤษ',
  description TEXT DEFAULT NULL COMMENT 'คำอธิบาย/วิธีเก็บตัวอย่าง',
  display_order INT UNSIGNED DEFAULT 0 COMMENT 'ลำดับการแสดงผล',
  is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน',
  created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  created_by VARCHAR(50) DEFAULT NULL,
  updated_by VARCHAR(50) DEFAULT NULL,

  INDEX idx_code (code),
  INDEX idx_active (is_active),
  INDEX idx_display_order (display_order)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='ประเภทสิ่งส่งตรวจ';

Columns

Column Type Nullable Description
id BIGINT UNSIGNED NO Primary Key
code VARCHAR(20) NO รหัสประเภท (BLOOD, URINE, STOOL) - UNIQUE
name_th VARCHAR(255) NO ชื่อภาษาไทย (เลือด, ปัสสาวะ)
name_en VARCHAR(255) NO ชื่อภาษาอังกฤษ (Blood, Urine)
description TEXT YES คำอธิบาย/วิธีเก็บตัวอย่าง
display_order INT UNSIGNED NO ลำดับการแสดงผล
is_active TINYINT(1) NO สถานะการใช้งาน
created_at DATETIME NO วันเวลาที่สร้าง
updated_at DATETIME NO วันเวลาที่แก้ไขล่าสุด
created_by VARCHAR(50) YES ผู้สร้างข้อมูล
updated_by VARCHAR(50) YES ผู้แก้ไขข้อมูล

Business Rules

  • code ต้อง UNIQUE และเป็น uppercase
  • description ใช้บันทึกวิธีการเก็บตัวอย่าง/ข้อควรระวัง

3. container_types (ประเภทหลอด/ภาชนะ)

ประเภทหลอดหรือภาชนะที่ใช้เก็บสิ่งส่งตรวจ เช่น EDTA, Plain, Fluoride

Schema

CREATE TABLE container_types (
  id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  code VARCHAR(20) NOT NULL UNIQUE COMMENT 'รหัสประเภทหลอด (EDTA, PLAIN)',
  name_th VARCHAR(255) NOT NULL COMMENT 'ชื่อภาษาไทย',
  name_en VARCHAR(255) NOT NULL COMMENT 'ชื่อภาษาอังกฤษ',
  color VARCHAR(20) DEFAULT NULL COMMENT 'สีจุกหลอด (Hex Color)',
  description TEXT DEFAULT NULL COMMENT 'คำอธิบาย/สารกันเลือดแข็ง',
  display_order INT UNSIGNED DEFAULT 0 COMMENT 'ลำดับการแสดงผล',
  is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน',
  created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  created_by VARCHAR(50) DEFAULT NULL,
  updated_by VARCHAR(50) DEFAULT NULL,

  INDEX idx_code (code),
  INDEX idx_active (is_active),
  INDEX idx_display_order (display_order)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='ประเภทหลอด/ภาชนะเก็บตัวอย่าง';

Columns

Column Type Nullable Description
id BIGINT UNSIGNED NO Primary Key
code VARCHAR(20) NO รหัสประเภทหลอด (EDTA, PLAIN, FLUORIDE) - UNIQUE
name_th VARCHAR(255) NO ชื่อภาษาไทย (จุกม่วง EDTA)
name_en VARCHAR(255) NO ชื่อภาษาอังกฤษ (EDTA Tube)
color VARCHAR(20) YES สีจุกหลอด (Hex #6B46C1)
description TEXT YES คำอธิบาย/สารกันเลือดแข็ง
display_order INT UNSIGNED NO ลำดับการแสดงผล
is_active TINYINT(1) NO สถานะการใช้งาน
created_at DATETIME NO วันเวลาที่สร้าง
updated_at DATETIME NO วันเวลาที่แก้ไขล่าสุด
created_by VARCHAR(50) YES ผู้สร้างข้อมูล
updated_by VARCHAR(50) YES ผู้แก้ไขข้อมูล

Business Rules

  • code ต้อง UNIQUE และเป็น uppercase
  • color เก็บสีจุกหลอดจริง (Purple=#6B46C1, Red=#DC2626)
  • description บันทึกชนิดสารกันเลือดแข็ง (Anticoagulant)

4. lab_items (รายการตรวจ Lab)

รายการตรวจ Lab แต่ละรายการ รองรับทั้ง In-house และ Out Lab

Schema

CREATE TABLE lab_items (
  id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  lab_code VARCHAR(50) NOT NULL UNIQUE COMMENT 'รหัสรายการตรวจ (CBC, FBS)',
  tmlt_code VARCHAR(20) DEFAULT NULL COMMENT 'รหัส TMLT (Thai Medical Lab Test Code)',
  loinc_code VARCHAR(20) DEFAULT NULL COMMENT 'รหัส LOINC (International Standard)',
  lab_name_th VARCHAR(255) NOT NULL COMMENT 'ชื่อรายการตรวจภาษาไทย',
  lab_name_en VARCHAR(255) NOT NULL COMMENT 'ชื่อรายการตรวจภาษาอังกฤษ',

  -- จัดหมวดหมู่
  category_id BIGINT UNSIGNED DEFAULT NULL COMMENT 'หมวดหมู่ (FK: lab_categories.id)',
  specimen_type_id BIGINT UNSIGNED DEFAULT NULL COMMENT 'ประเภทสิ่งส่งตรวจ (FK: specimen_types.id)',
  container_type_id BIGINT UNSIGNED DEFAULT NULL COMMENT 'ประเภทหลอด (FK: container_types.id)',

  -- ข้อมูลทั่วไป
  specimen_volume DECIMAL(10,2) DEFAULT NULL COMMENT 'ปริมาณสิ่งส่งตรวจที่ต้องการ (ml)',
  unit VARCHAR(50) DEFAULT NULL COMMENT 'หน่วยของผลตรวจ (mg/dL, mmol/L)',
  default_value VARCHAR(100) DEFAULT NULL COMMENT 'ค่าพื้นฐาน (ค่ามาตรฐาน)',
  method TEXT DEFAULT NULL COMMENT 'วิธีการตรวจ',
  turn_around_time INT UNSIGNED DEFAULT NULL COMMENT 'ระยะเวลาออกผล (นาที)',

  -- ราคา
  price DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT 'ราคาขาย (บาท)',
  service_category_code VARCHAR(50) DEFAULT NULL COMMENT 'หมวดหมู่ค่าบริการ (เชื่อมโยงระบบการเงิน)',
  cost DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT 'ต้นทุน (บาท)',

  -- Out Lab Support
  is_out_lab TINYINT(1) DEFAULT 0 COMMENT 'เป็น Out Lab หรือไม่ (0=In-house, 1=Out Lab)',
  out_lab_name VARCHAR(255) DEFAULT NULL COMMENT 'ชื่อห้อง Lab ภายนอก',
  out_lab_code VARCHAR(50) DEFAULT NULL COMMENT 'รหัสรายการของ Out Lab',
  out_lab_cost DECIMAL(10,2) DEFAULT NULL COMMENT 'ค่าใช้จ่ายจริงที่จ่ายให้ Out Lab',

  -- สถานะ
  is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน',
  display_order INT UNSIGNED DEFAULT 0 COMMENT 'ลำดับการแสดงผล',

  -- Audit
  created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  created_by VARCHAR(50) DEFAULT NULL,
  updated_by VARCHAR(50) DEFAULT NULL,

  -- Indexes
  INDEX idx_lab_code (lab_code),
  INDEX idx_category (category_id),
  INDEX idx_specimen_type (specimen_type_id),
  INDEX idx_container_type (container_type_id),
  INDEX idx_is_out_lab (is_out_lab),
  INDEX idx_active (is_active),
  INDEX idx_display_order (display_order),

  -- Foreign Keys
  CONSTRAINT fk_lab_item_category 
    FOREIGN KEY (category_id) REFERENCES lab_categories(id) 
    ON DELETE SET NULL ON UPDATE CASCADE,
  CONSTRAINT fk_lab_item_specimen 
    FOREIGN KEY (specimen_type_id) REFERENCES specimen_types(id) 
    ON DELETE SET NULL ON UPDATE CASCADE,
  CONSTRAINT fk_lab_item_container 
    FOREIGN KEY (container_type_id) REFERENCES container_types(id) 
    ON DELETE SET NULL ON UPDATE CASCADE

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='รายการตรวจ Lab';

Columns

Column Type Nullable Description
id BIGINT UNSIGNED NO Primary Key
lab_code VARCHAR(50) NO รหัสรายการตรวจ - UNIQUE
tmlt_code VARCHAR(20) YES รหัส TMLT (ไทย)
loinc_code VARCHAR(20) YES รหัส LOINC (สากล)
lab_name_th VARCHAR(255) NO ชื่อรายการตรวจภาษาไทย
lab_name_en VARCHAR(255) NO ชื่อรายการตรวจภาษาอังกฤษ
category_id BIGINT UNSIGNED YES FK → lab_categories
specimen_type_id BIGINT UNSIGNED YES FK → specimen_types
container_type_id BIGINT UNSIGNED YES FK → container_types
specimen_volume DECIMAL(10,2) YES ปริมาณสิ่งส่งตรวจ (ml)
unit VARCHAR(50) YES หน่วยของผลตรวจ (mg/dL, mmol/L)
default_value VARCHAR(100) YES ค่าพื้นฐาน (ค่ามาตรฐาน)
method TEXT YES วิธีการตรวจ
turn_around_time INT UNSIGNED YES ระยะเวลาออกผล (นาที)
price DECIMAL(10,2) NO ราคาขาย (บาท)
service_category_code VARCHAR(50) YES หมวดหมู่ค่าบริการ (เชื่อมโยงระบบการเงิน)
cost DECIMAL(10,2) NO ต้นทุน (บาท)
unit VARCHAR(50) YES หน่วยของผลตรวจ
method TEXT YES วิธีการตรวจ
turn_around_time INT UNSIGNED YES ระยะเวลาออกผล (นาที)
price DECIMAL(10,2) NO ราคาขาย (บาท)
cost DECIMAL(10,2) NO ต้นทุน (บาท)
is_out_lab TINYINT(1) NO เป็น Out Lab หรือไม่ (0/1)
out_lab_name VARCHAR(255) YES ชื่อห้อง Lab ภายนอก
out_lab_code VARCHAR(50) YES รหัสรายการของ Out Lab
out_lab_cost DECIMAL(10,2) YES ค่าใช้จ่ายจ่ายให้ Out Lab
is_active TINYINT(1) NO สถานะการใช้งาน
display_order INT UNSIGNED NO ลำดับการแสดงผล

Business Rules

  • lab_code ต้อง UNIQUE และเป็น uppercase
  • ถ้า is_out_lab = 1 ต้องระบุ out_lab_name และ out_lab_cost
  • pricecost (ป้องกันขายขาดทุน)
  • out_lab_cost คือราคาที่จ่ายให้ Out Lab (ต้นทุนจริง)
  • turn_around_time เป็นนาที (480 = 8 ชั่วโมง)

5. lab_panels (กลุ่มรายการตรวจ / Profiles)

กลุ่มรายการตรวจที่มักสั่งร่วมกัน (Panel/Profile) เช่น CBC + ESR, Lipid Profile

Schema

CREATE TABLE lab_panels (
  id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  panel_code VARCHAR(50) NOT NULL UNIQUE COMMENT 'รหัส Panel (LFT, RFT)',
  panel_name_th VARCHAR(255) NOT NULL COMMENT 'ชื่อ Panel ภาษาไทย',
  panel_name_en VARCHAR(255) NOT NULL COMMENT 'ชื่อ Panel ภาษาอังกฤษ',
  description TEXT DEFAULT NULL COMMENT 'คำอธิบาย/ข้อบ่งชี้',

  -- ราคา
  price DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT 'ราคาขาย Panel (บาท)',
  cost DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT 'ต้นทุนรวม (คำนวณจาก items)',

  -- สถานะ
  is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน',
  display_order INT UNSIGNED DEFAULT 0 COMMENT 'ลำดับการแสดงผล',

  -- Audit
  created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  created_by VARCHAR(50) DEFAULT NULL,
  updated_by VARCHAR(50) DEFAULT NULL,

  -- Indexes
  INDEX idx_panel_code (panel_code),
  INDEX idx_active (is_active),
  INDEX idx_display_order (display_order)

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='กลุ่มรายการตรวจ (Lab Panels)';

Columns

Column Type Nullable Description
id BIGINT UNSIGNED NO Primary Key
panel_code VARCHAR(50) NO รหัส Panel - UNIQUE
panel_name_th VARCHAR(255) NO ชื่อ Panel ภาษาไทย
panel_name_en VARCHAR(255) NO ชื่อ Panel ภาษาอังกฤษ
description TEXT YES คำอธิบาย/ข้อบ่งชี้
price DECIMAL(10,2) NO ราคาขาย Panel
cost DECIMAL(10,2) NO ต้นทุนรวม
is_active TINYINT(1) NO สถานะการใช้งาน
display_order INT UNSIGNED NO ลำดับการแสดงผล

Business Rules

  • panel_code ต้อง UNIQUE และเป็น uppercase
  • price ควรต่ำกว่าผลรวมราคา items แยก (เพื่อจูงใจให้สั่ง Panel)
  • cost คำนวณจากผลรวม cost ของ items ใน Panel
  • Panel อาจมีทั้ง In-house และ Out Lab items ปนกัน

6. lab_panel_items (รายการตรวจใน Panel)

Junction Table เชื่อมระหว่าง lab_panels และ lab_items (Many-to-Many)

Schema

CREATE TABLE lab_panel_items (
  id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  panel_id BIGINT UNSIGNED NOT NULL COMMENT 'FK: lab_panels.id',
  lab_item_id BIGINT UNSIGNED NOT NULL COMMENT 'FK: lab_items.id',
  display_order INT UNSIGNED DEFAULT 0 COMMENT 'ลำดับการแสดงผลใน Panel',

  created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,

  -- Indexes
  INDEX idx_panel (panel_id),
  INDEX idx_lab_item (lab_item_id),
  UNIQUE KEY uk_panel_item (panel_id, lab_item_id),

  -- Foreign Keys
  CONSTRAINT fk_panel_item_panel 
    FOREIGN KEY (panel_id) REFERENCES lab_panels(id) 
    ON DELETE CASCADE ON UPDATE CASCADE,
  CONSTRAINT fk_panel_item_lab 
    FOREIGN KEY (lab_item_id) REFERENCES lab_items(id) 
    ON DELETE CASCADE ON UPDATE CASCADE

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='รายการตรวจใน Panel (Junction Table)';

Columns

Column Type Nullable Description
id BIGINT UNSIGNED NO Primary Key
panel_id BIGINT UNSIGNED NO FK → lab_panels
lab_item_id BIGINT UNSIGNED NO FK → lab_items
display_order INT UNSIGNED NO ลำดับการแสดงผลใน Panel
created_at DATETIME NO วันเวลาที่สร้าง

Business Rules

  • (panel_id, lab_item_id) ต้อง UNIQUE (ห้ามมี item ซ้ำใน panel เดียวกัน)
  • ON DELETE CASCADE: ลบ Panel → ลบ items ใน Panel
  • display_order ใช้จัดเรียงรายการใน Panel UI

7. lab_normal_ranges (ค่าปกติตามอายุ/เพศ)

เก็บค่าปกติของแต่ละรายการตรวจ แยกตามอายุ/เพศ

Schema

CREATE TABLE lab_normal_ranges (
  id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  lab_item_id BIGINT UNSIGNED NOT NULL COMMENT 'FK: lab_items.id',

  -- ช่วงอายุ (เก็บเป็นวัน)
  age_min_days INT UNSIGNED DEFAULT NULL COMMENT 'อายุต่ำสุด (วัน) NULL=ไม่จำกัด',
  age_max_days INT UNSIGNED DEFAULT NULL COMMENT 'อายุสูงสุด (วัน) NULL=ไม่จำกัด',

  -- เพศ
  gender ENUM('M', 'F', 'ALL') DEFAULT 'ALL' COMMENT 'เพศ (M=ชาย, F=หญิง, ALL=ทั้งหมด)',

  -- ประเภทค่าปกติ
  range_type ENUM('NUMERIC', 'TEXT', 'POSSIBLE_VALUES') NOT NULL DEFAULT 'NUMERIC' 
    COMMENT 'ประเภท (NUMERIC=ตัวเลข, TEXT=ข้อความ, POSSIBLE_VALUES=เลือกค่า)',

  -- ค่าปกติ (ตัวเลข)
  min_value DECIMAL(15,4) DEFAULT NULL COMMENT 'ค่าต่ำสุด (สำหรับ NUMERIC)',
  max_value DECIMAL(15,4) DEFAULT NULL COMMENT 'ค่าสูงสุด (สำหรับ NUMERIC)',

  -- ค่าปกติ (ข้อความ)
  text_value TEXT DEFAULT NULL COMMENT 'ค่าปกติแบบข้อความ (สำหรับ TEXT)',
  possible_values TEXT DEFAULT NULL COMMENT 'ค่าที่เป็นไปได้ (JSON array สำหรับ POSSIBLE_VALUES)',

  -- Audit
  created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  created_by VARCHAR(50) DEFAULT NULL,
  updated_by VARCHAR(50) DEFAULT NULL,

  -- Indexes
  INDEX idx_lab_item (lab_item_id),
  INDEX idx_age_range (age_min_days, age_max_days),
  INDEX idx_gender (gender),

  -- Foreign Keys
  CONSTRAINT fk_normal_range_lab_item 
    FOREIGN KEY (lab_item_id) REFERENCES lab_items(id) 
    ON DELETE CASCADE ON UPDATE CASCADE

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='ค่าปกติตามอายุ/เพศ';

Columns

Column Type Nullable Description
id BIGINT UNSIGNED NO Primary Key
lab_item_id BIGINT UNSIGNED NO FK → lab_items
age_min_days INT UNSIGNED YES อายุต่ำสุด (วัน) NULL=ไม่จำกัด
age_max_days INT UNSIGNED YES อายุสูงสุด (วัน) NULL=ไม่จำกัด
gender ENUM NO เพศ (M/F/ALL)
range_type ENUM NO ประเภท (NUMERIC/TEXT/POSSIBLE_VALUES)
min_value DECIMAL(15,4) YES ค่าต่ำสุด (NUMERIC)
max_value DECIMAL(15,4) YES ค่าสูงสุด (NUMERIC)
text_value TEXT YES ค่าปกติข้อความ (TEXT)
possible_values TEXT YES ค่าที่เป็นไปได้ JSON (POSSIBLE_VALUES)

Business Rules

  • อายุเก็บเป็นวัน (days) เพื่อความแม่นยำ (1 ปี = 365 วัน)
  • NULL ใน age_min_days = ไม่จำกัดอายุขั้นต่ำ
  • NULL ใน age_max_days = ไม่จำกัดอายุสูงสุด
  • range_type = NUMERIC: ใช้ min_value, max_value
  • range_type = TEXT: ใช้ text_value (เช่น "Negative")
  • range_type = POSSIBLE_VALUES: ใช้ possible_values (JSON array)

ตัวอย่าง Age Ranges

Age Description age_min_days age_max_days
ทารก 0-1 ปี 0 365
เด็ก 1-12 ปี 366 4380
วัยรุ่น 13-18 ปี 4745 6570
ผู้ใหญ่ ≥18 ปี 6571 NULL
ทุกอายุ NULL NULL

8. lab_critical_values (ค่าวิกฤต)

เก็บค่าวิกฤต (Critical/Panic Values) ที่ต้องแจ้งแพทย์ทันที

Schema

CREATE TABLE lab_critical_values (
  id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  lab_item_id BIGINT UNSIGNED NOT NULL COMMENT 'FK: lab_items.id',

  -- ค่าวิกฤต (Critical)
  critical_low DECIMAL(15,4) DEFAULT NULL COMMENT 'ค่าต่ำวิกฤต (Critical Low)',
  critical_high DECIMAL(15,4) DEFAULT NULL COMMENT 'ค่าสูงวิกฤต (Critical High)',

  -- ค่าวิกฤตฉุกเฉิน (Panic)
  panic_low DECIMAL(15,4) DEFAULT NULL COMMENT 'ค่าต่ำฉุกเฉิน (Panic Low)',
  panic_high DECIMAL(15,4) DEFAULT NULL COMMENT 'ค่าสูงฉุกเฉิน (Panic High)',

  -- การแจ้งเตือน
  alert_message TEXT DEFAULT NULL COMMENT 'ข้อความแจ้งเตือนเมื่อพบค่าวิกฤต',
  action_required TEXT DEFAULT NULL COMMENT 'การปฏิบัติที่ต้องทำ',
  notify_immediately TINYINT(1) DEFAULT 1 COMMENT 'แจ้งแพทย์ทันที (0=ไม่ต้อง, 1=ต้องแจ้ง)',

  -- Audit
  created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  created_by VARCHAR(50) DEFAULT NULL,
  updated_by VARCHAR(50) DEFAULT NULL,

  -- Indexes
  INDEX idx_lab_item (lab_item_id),
  UNIQUE KEY uk_lab_item (lab_item_id),

  -- Foreign Keys
  CONSTRAINT fk_critical_value_lab_item 
    FOREIGN KEY (lab_item_id) REFERENCES lab_items(id) 
    ON DELETE CASCADE ON UPDATE CASCADE

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='ค่าวิกฤต (Critical/Panic Values)';

Columns

Column Type Nullable Description
id BIGINT UNSIGNED NO Primary Key
lab_item_id BIGINT UNSIGNED NO FK → lab_items (UNIQUE)
critical_low DECIMAL(15,4) YES ค่าต่ำวิกฤต
critical_high DECIMAL(15,4) YES ค่าสูงวิกฤต
panic_low DECIMAL(15,4) YES ค่าต่ำฉุกเฉิน
panic_high DECIMAL(15,4) YES ค่าสูงฉุกเฉิน
alert_message TEXT YES ข้อความแจ้งเตือน
action_required TEXT YES การปฏิบัติที่ต้องทำ
notify_immediately TINYINT(1) NO แจ้งแพทย์ทันที

Business Rules

  • lab_item_id ต้อง UNIQUE (1 รายการตรวจ = 1 ชุดค่าวิกฤต)
  • Critical: ค่าที่ผิดปกติมาก ต้องติดตามเร่งด่วน
  • Panic: ค่าที่อันตรายถึงชีวิต ต้องแจ้งแพทย์ทันที
  • Logic: panic_low < critical_low < normal < critical_high < panic_high
  • notify_immediately = 1: ระบบต้องส่ง alert ไปหาแพทย์ทันที

ตัวอย่างค่าวิกฤต

รายการ Critical Low Critical High Panic Low Panic High
Glucose 50 400 40 500
Potassium 2.8 5.5 2.5 6.0
Hemoglobin 7.0 18.0 5.0 20.0

9. lab_item_coverage (ความครอบคลุมสิทธิ์)

เก็บความครอบคลุมรายการตรวจ Lab กับสิทธิการรักษา (ราคา, ค่าร่วมจ่าย, PA)

Schema

CREATE TABLE lab_item_coverage (
  id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,

  -- Lab Item Reference
  lab_item_id VARCHAR(50) NOT NULL COMMENT 'รหัสรายการตรวจ Lab (FK: lab_items.lab_code)',
  lab_item_name_th VARCHAR(255) NOT NULL COMMENT 'ชื่อรายการตรวจภาษาไทย (Auto-fetch)',
  lab_item_name_en VARCHAR(255) NOT NULL COMMENT 'ชื่อรายการตรวจภาษาอังกฤษ (Auto-fetch)',

  -- Eligibility Reference
  eligibility_code VARCHAR(20) NOT NULL COMMENT 'รหัสสิทธิ (UC, SSO, CSMBS, PRIVATE, CASH, LGO)',
  eligibility_name_th VARCHAR(255) NOT NULL COMMENT 'ชื่อสิทธิภาษาไทย (Auto-fetch)',
  eligibility_name_en VARCHAR(255) NOT NULL COMMENT 'ชื่อสิทธิภาษาอังกฤษ (Auto-fetch)',

  -- Coverage Details
  coverage_status ENUM('covered', 'not_covered', 'prior_auth_required') DEFAULT 'covered' 
    COMMENT 'สถานะความคุ้มครอง',
  price DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT 'ราคารายการตรวจสำหรับสิทธินี้ (บาท)',

  -- Copay Options
  copay_amount DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT 'ค่าร่วมจ่ายคงที่ (บาท)',
  copay_percent DECIMAL(5,2) NOT NULL DEFAULT 0.00 COMMENT 'ค่าร่วมจ่ายเป็นเปอร์เซ็นต์ (%)',

  -- Prior Authorization
  requires_prior_auth TINYINT(1) DEFAULT 0 COMMENT 'ต้องขออนุมัติก่อน (PA)',
  prior_auth_criteria TEXT DEFAULT NULL COMMENT 'เกณฑ์การขออนุมัติ PA',

  -- Limits
  max_times_per_year INT UNSIGNED DEFAULT NULL COMMENT 'จำนวนครั้งสูงสุดต่อปี (NULL=ไม่จำกัด)',
  notes TEXT DEFAULT NULL COMMENT 'หมายเหตุเพิ่มเติม',

  -- Effective Period
  effective_date DATE DEFAULT NULL COMMENT 'วันที่เริ่มใช้งาน',
  expiry_date DATE DEFAULT NULL COMMENT 'วันที่หมดอายุ (NULL=ไม่หมดอายุ)',

  -- Status
  is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน',

  -- Audit
  created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  created_by VARCHAR(50) DEFAULT NULL COMMENT 'ผู้สร้างข้อมูล',
  updated_by VARCHAR(50) DEFAULT NULL COMMENT 'ผู้แก้ไขข้อมูล',

  -- Indexes
  UNIQUE KEY uk_lab_eligibility (lab_item_id, eligibility_code),
  INDEX idx_lab_item (lab_item_id),
  INDEX idx_eligibility (eligibility_code),
  INDEX idx_coverage_status (coverage_status),
  INDEX idx_prior_auth (requires_prior_auth),
  INDEX idx_active (is_active)

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci 
  COMMENT='ความครอบคลุมรายการตรวจ Lab กับสิทธิการรักษา';

Columns

Column Type Nullable Description
id BIGINT UNSIGNED NO Primary Key
lab_item_id VARCHAR(50) NO รหัสรายการตรวจ Lab (FK to lab_items.lab_code)
lab_item_name_th VARCHAR(255) NO ชื่อรายการตรวจภาษาไทย (Auto-populated)
lab_item_name_en VARCHAR(255) NO ชื่อรายการตรวจภาษาอังกฤษ (Auto-populated)
eligibility_code VARCHAR(20) NO รหัสสิทธิ (UC, SSO, CSMBS, PRIVATE, CASH, LGO)
eligibility_name_th VARCHAR(255) NO ชื่อสิทธิภาษาไทย (Auto-populated)
eligibility_name_en VARCHAR(255) NO ชื่อสิทธิภาษาอังกฤษ (Auto-populated)
coverage_status ENUM NO สถานะความคุ้มครอง (covered/not_covered/prior_auth_required)
price DECIMAL(10,2) NO ราคารายการตรวจสำหรับสิทธินี้ (บาท)
copay_amount DECIMAL(10,2) NO ค่าร่วมจ่ายคงที่ (บาท)
copay_percent DECIMAL(5,2) NO ค่าร่วมจ่ายเป็นเปอร์เซ็นต์ (0-100%)
requires_prior_auth TINYINT(1) NO ต้องขออนุมัติก่อน (PA)
prior_auth_criteria TEXT YES เกณฑ์การขออนุมัติ PA
max_times_per_year INT UNSIGNED YES จำนวนครั้งสูงสุดต่อปี (NULL=ไม่จำกัด)
notes TEXT YES หมายเหตุเพิ่มเติม
effective_date DATE YES วันที่เริ่มใช้งาน
expiry_date DATE YES วันที่หมดอายุ (NULL=ไม่หมดอายุ)
is_active TINYINT(1) NO สถานะการใช้งาน

Business Rules

  • Unique Constraint: (lab_item_id, eligibility_code) ต้อง UNIQUE
  • Coverage Status:
  • covered: คุ้มครอง ตัดสิทธิได้เลย
  • not_covered: ไม่คุ้มครอง ผู้ป่วยจ่ายเต็ม
  • prior_auth_required: ต้องขออนุมัติก่อน (PA)
  • Price Calculation: ราคาที่ผู้ป่วยจ่าย = price × (copay_percent/100) + copay_amount
  • Prior Authorization: ถ้า requires_prior_auth = 1 → แพทย์ต้องขออนุมัติก่อน
  • Annual Limits: max_times_per_year = จำกัดครั้ง, NULL = ไม่จำกัด
  • Auto-Populated: ชื่อ Lab Item และชื่อสิทธิ ดึงมาจาก master data อัตโนมัติ

ตัวอย่างข้อมูล

UC (บัตรทอง) - คุ้มครองเต็ม

INSERT INTO lab_item_coverage (
  lab_item_id, lab_item_name_th, eligibility_code, eligibility_name_th,
  coverage_status, price, copay_amount, copay_percent
) VALUES 
('L081051', 'นับเม็ดเลือดขาว WBC', 'UC', 'สิทธิ UC บัตรทอง', 'covered', 50.00, 0.00, 0.00),
('L082001', 'น้ำตาลในเลือด FBS', 'UC', 'สิทธิ UC บัตรทอง', 'covered', 60.00, 0.00, 0.00);

SSO (ประกันสังคม) - มีค่าร่วมจ่าย 30 บาท

INSERT INTO lab_item_coverage (
  lab_item_id, lab_item_name_th, eligibility_code, eligibility_name_th,
  coverage_status, price, copay_amount, copay_percent
) VALUES 
('L081051', 'นับเม็ดเลือดขาว WBC', 'SSO', 'สิทธิประกันสังคม SSO', 'covered', 50.00, 30.00, 0.00),
('L082001', 'น้ำตาลในเลือด FBS', 'SSO', 'สิทธิประกันสังคม SSO', 'covered', 60.00, 30.00, 0.00);

CSMBS (ข้าราชการ) - ต้องขออนุมัติ PA

INSERT INTO lab_item_coverage (
  lab_item_id, lab_item_name_th, eligibility_code, eligibility_name_th,
  coverage_status, price, requires_prior_auth, prior_auth_criteria
) VALUES 
('L082015', 'HbA1C', 'CSMBS', 'สิทธิข้าราชการ CSMBS', 'prior_auth_required', 300.00, 1,
 'ต้องมี HbA1C > 7% หรือมีประวัติโรคเบาหวาน');

Integration with Other Tables

-- ดึงข้อมูล Lab Item + Coverage สำหรับสิทธิ UC
SELECT 
  li.lab_code,
  li.lab_name_th,
  lc.coverage_status,
  lc.price,
  lc.copay_amount,
  lc.requires_prior_auth
FROM lab_items li
LEFT JOIN lab_item_coverage lc 
  ON li.lab_code = lc.lab_item_id 
  AND lc.eligibility_code = 'UC'
WHERE li.is_active = 1 AND lc.is_active = 1;

🔐 Security & Performance

Indexes Strategy

  • Primary Keys: ทุก table มี AUTO_INCREMENT id
  • Unique Keys: code fields ทั้งหมด (lab_code, panel_code, etc.)
  • Foreign Keys: เพื่อ referential integrity
  • Search Indexes: code, name_th, is_active
  • Composite Indexes: (panel_id, lab_item_id) ใน junction table

Data Integrity

  • Foreign Key Constraints: ป้องกัน orphaned records
  • ON DELETE CASCADE: Junction tables (lab_panel_items)
  • ON DELETE SET NULL: Optional references (category_id, specimen_type_id)
  • CHECK Constraints: (เพิ่มได้ในอนาคตสำหรับ price >= cost)

Backup Strategy

  • Daily Full Backup: ทุกวัน 00:00
  • Hourly Incremental: ระหว่างวัน
  • Retention: เก็บ 30 วัน

Next: ดู RELATIONSHIPS.md สำหรับ ERD และความสัมพันธ์ระหว่าง Tables