Categories
Computer Diary

#16 – เขียนโค้ด DBML เพื่อวาด ER Diagram

ER Diagram คือแบบจำลองอธิบายโครงสร้าง และความสัมพันธ์ของข้อมูลด้วย Entity และ Relationship ในบทความนี้เราจะทำโปรเจ็คเขียนโค้ดภาษา DBML เพื่อวาด ER Diagram ออกมา

ER Diagram (Entity Relation Diagram) คือแบบจำลองที่ใช้อธิบายโครงสร้างของ Database ที่ออกมาเป็นรูปภาพ เพื่ออธิบายโครงสร้างและความสัมพันธ์ของข้อมูล ประกอบไปด้วย 2 ส่วน ได้แก่ Entity และ Relationship.

Entity

Entity เป็นกลุ่มของสิ่งต่าง ๆ ที่เราสนใจที่เราเก็บข้อมูลภายใต้ Database โดยตัวอย่างของ Entity ได้แก่ บุคคล (เช่นนักเรียน) สิ่งของ (เช่นใบเสร็จ) และเหตุการณ์ (เช่นการทำธุรกรรม) เป็นต้น ที่มีลักษณะเป็นสี่เหลี่ยมมุมมนที่มีชื่ออยู่ข้างบน และมีคุณสมบัติ (Property) หรือ Attribute อยู่ข้างใน ที่นำมาอธิบาย Entity โดย อธิบายถึงชื่อ และชนิดของตัวแปร [1]

ทั่ว ๆ ไป ข้อมูล Entity มักจะอยู่ในประเภท Strong Entity ที่เป็นข้อมูลที่ไม่ขึ้นกับ Entity อื่น ซึ่งแตกต่างกับ Weak Entity ที่เป็นข้อมูลที่ขึ้นกับ Entity อันอื่น (หรือ Existence dependent) กรณีที่ข้อมูลหลักถูกลบ หรือเปลี่ยนข้อมูล ข้อมูลก็จะกระทบไปยัง Weak Entity [2]

การใช้งานเรานำ Entity มาอธิบาย Table ใน Database ตามตัวอย่างด้านล่างนี้

Entity (ภาพจาก Wikimedia)

สำหรับ Attribute ที่เป็นคีย์หลักที่มีค่าที่ไม่ซ้ำกัน และไม่เป็นค่าช่องว่าง (Null) ส่วนนี้เรียกว่า Primary Key โดยเราสามารถนำคอลัมน์ Primary Key นี้ไปสร้างความสัมพันธ์ (Relation) กับตารางอื่น ๆ ผ่านการกำหนดคอลัมน์ที่เรียกว่า Foreign Key [3] โดยเราใช้ภาพด้านล่างนี้เป็นตัวอย่าง

  • ตัว actor_id และ film_id ใน Table actor และ film เป็น Primary Key
  • ส่วน actor_id และ film_id ใน Table actor_film_mapping เป็น Foreign Key
ภาพอธิบายความสัมพันธ์ในตาราง (ภาพจาก Wikimedia)

Relationship

Relationship เป็นการอธิบายความสัมพันธ์ระหว่าง Entity ต่าง ๆ ในฐานข้อมูล โดยแบ่งตามจำนวน Entity ที่มีความสัมพันธ์กันได้ตามด้านล่างนี้ [4]

  1. Binary Relationship ที่มีความสัมพันธ์แบบ 2 Entity
  2. Ternary Relationship ที่มีความสัมพันธ์มากกว่า 2 Entity
  3. Recursive Relationship หรือ Self Relationship หรือ Unary Relationship ที่มีความสัมพันธ์กับ Entity ตนเอง
แผนภาพแสดง Relationship ตามจำนวน Entity

นอกจากนี้ เรายังแบ่ง Relationship ตามจำนวนข้อมูลระหว่าง Entity ที่มี Relationship กัน (Cardinality Ratio) ที่มีด้วยกันทั้งหมด 4 แบบ [4, 5] ได้แก่

  1. One-to-one Relationship (1:1) เป็นการแสดงความสัมพันธ์ของข้อมูล Entity A 1 รายการ และ Entity B ไม่เกิน 1 รายการ
  2. One-to-many Relationship (1:N) เป็นการแสดงความสัมพันธ์ของข้อมูล Entity A 1 รายการ และ Entity B ได้มากกว่า 1 รายการ
  3. Many-to-one Relationship (N:1) เป็นการแสดงความสัมพันธ์ของข้อมูล Entity A 1 รายการ ที่สัมพันธ์กับ Entity B 1 รายการ ในขณะที่ข้อมูล Entity B 1 รายการสัมพันธ์กับ Entity A มากกว่า 1 รายการ
  4. Many-to-many Relationship (M:N) เป็นการแสดงความสัมพันธ์ของข้อมูล Entity A 1 รายการ และ Entity B ได้มากกว่า 1 รายการ และ Entity B 1 รายการ และ Entity A ได้มากกว่า 1 รายการ

ER Diagram Model

ER Diagram Model [6, 7] แบ่งได้ทั้งหมด 3 ประเภทตามระดับของแนวคิด (Abstraction)

Conceptual ERD (Conceptual Data Model) เป็นการออกแบบฐานข้อมูลในระดับแนวคิดเพื่ออธิบายตามความต้องการของผู้ใช้ฐานข้อมูล ที่ออกแบบ Entity และ Relationship ระหว่าง Entity ร่วมกับกำหนดข้อบังคับของข้อมูล

Conceptual Data Model (ภาพจาก Wikimedia)

Logical ERD (Logical Data Model) เป็น Data Model ที่ใช้บ่อยที่สุด ที่เป็นการแสดงรายละเอียดเพิ่มเติมนอกเหนือจาก Conceptual ERD ที่อธิบายถึง Entities, Relationships, Attributes กับ Primary Key และ Foreign Key โดยไม่ได้กล่าวถึง Database และ Platform ที่ใช้ในการประยุกต์ใช้งาน Model นี้

Logical Data Model (ภาพจาก Wikimedia)

Physical ERD (Physical Data Model) เป็น Data Model ที่ต่อยอดมาจาก Logical Data Model สำหรับการใช้งานใน Relational Database Management System (RDBMS) ที่ขึ้นอยู่กับ Database แต่ละเจ้า โดยระบุถึงชื่อ Table และชื่อ Column แทนที่จะใช้ชื่อ Entity และ Attributes นอกจากนี้เรายังระบุถึงชนิดของข้อมูลในแต่ละ Column ได้เลย

ตัวแผนภาพลักษณะน้เราสามารถนำไปใช้งานต่อได้โดยเขียนโค้ดผ่านการใช้คำสั่ง SQL (Structured Query Language)

Physical Data Model (ภาพจาก Wikimedia)

เราสามารถสรุปข้อแตกต่างได้ตามตาราง [7] ด้านล่างนี้

FeatureConceptual
Data Model
Logical
Data Model
Physical
Data Model
Entity Namesระบุระบุ
Entity Relationshipsระบุระบุ
Attributesระบุ
Primary Keysระบุระบุ
Foreign Keysระบุระบุ
Table Namesระบุ
Column Namesระบุ
Column Data Typesระบุ

Database Markup Language (DBML)

DBML หรือ Database Markup Language เป็นภาษาคอมพิวเตอร์ที่ประยุกต์ใช้สำหรับงานใดงานหนึ่ง (Domain-specific Language – DSL) ที่ออกแบบมาเพื่อใช้สำหรับ Define และ Document ลักษณะข้อมูล และโครงสร้างในตาราง (Database Schema and Structure) [8]

ภาษานี้มีที่มาจากเว็บ dbdiagram.io ที่ทางทีมงานต้องการสร้างภาษาเขียนโค้ดขึ้นมาให้เป็นภาษาที่ดี และเรียบง่ายต่อการออกแบบ และการสร้างโครงสร้างของ Database โดยตัวภาษานี้ออกแบบมาให้เป็น Opensource อีกด้วย

วัตถุประสงค์ของ DBML คือเป็นภาษาที่เรียบง่าย (Simple) อ่านง่าย (Highly-readable) และ Consistent โดยมีตัวคำสั่งคอมมานไลน์ที่ได้รับการออกแบบมาเพื่อแปลงโค้ดระหว่าง DBML และ SQL

ภาษานี้ได้รับการออกแบบมาเพื่อแก้ปัญหา Database ของโครงการขนาดใหญ่ที่ซับซ้อน โดยวาดออกมาเป็นภาพคร่าว ๆ ของ Database ของทั้งโครงการ เพื่อให้คนเข้าใจข้อมูลในแต่ละ Column และ Relationship ได้ง่าย และลดความยุ่งยาก และความ Outdated ของโค้ดที่เขียนด้วยภาษา SQL ที่ใช้คำสั่ง DDL (Data Definition Language)

DDL (Data Definition Language) เป็นกลุ่มคำสั่งในภาษา SQL ที่มีหน้าที่สร้าง แก้ไข จัดการ และลบ Table ใน Database โดยตัวอย่างคำสั่งที่จัดอยู่ในกลุ่มนี้คือ CREATE, ALTER TABLE, TRUNCATE และ DROP

ตัวโค้ด DBML มีเครื่องมือมาให้พร้อมเสร็จสรรพ โดยให้เครื่องมืออย่าง

  1. Free DB Visualizer อย่างเว็บ dbdiagram.io
  2. Free Documentation Reader อย่างเว็บ dbdocs.io
  3. คอมมานไลน์เพื่อแปลงคำสั่งจาก SQL ไป DBML และในทางกลับกัน
  4. ไลบรารีสำหรับการแปลงโค้ดกลับไปมาระหว่าง SQL และ DBML

Syntax

ลักษณะการเขียนโค้ดของ DBML ได้รับการออกแบบมาเพื่อให้คนเขียนโค้ดสำหรับการออกแบบ Database Schema and Structure ได้ง่าย โดยแบ่งออกเป็น 7 ส่วน ได้แก่

  • Project
  • Table
  • Relationship & Foreign Key
  • Comment
  • Note
  • Enum
  • TableGroup

Project

ส่วนแรก เป็นการเขียนโค้ดเพื่อกำหนด Project ของการออกแบบฐานข้อมูล โดยเรากำหนดรายละเอียด project ได้โดยการเขียนโค้ดได้ตามด้านล่างนี้

Project project_name {
    database_type: 'MySQL'
    Note: 'Description of this project'
}

โดย

  • project_name เป็นการกำหนดชื่อ Project
  • database_type เป็นการกำหนดเครื่องมือ Database ที่ต้องการ ตัวอย่างเช่น MySQL และ PostgreSQL เป็นต้น
  • Note เป็นการใส่คำอธิบายของ Project

Table

ต่อมา เป็นการเขียนโค้ดเพื่อกำหนด Table ใน Database โดยเราสามารถเขียนโค้ดได้ตามด้านล่างนี้

Table table_name {
  column_name column_type [column_settings]
}

โดย

  • schema_name เป็นการกำหนดชื่อ Schema ของ Table จุดนี้ถ้าไม่ได้พิมพ์ชื่อ Schema ตัวระบบจะกำหนดให้เป็น public
  • table_name คือชื่อของ Table
  • column_name คือชื่อคอลัมน์
  • column_type คือชนิดของข้อมูลในคอลัมน์นั้น ๆ
  • column_settings เป็นการตั้งค่าในแต่ละคอลัมน์ โดยลักษณะฟอร์แมตทั่วไปของการตั้งค่าทำได้โดย [setting1: value1, setting2: value2, …] การตั้งค่ามีหลายรูปแบบ ได้แก่
    • note: ‘xxx’ เป็นการใส่คำอธิบายเพิ่มรายละเอียดในคอลัมน์นั้น ๆ
    • primary key หรือ pk เป็นการกำหนดให้คอลัมน์นั้น ๆ เป็น Primary Key
    • null/not null เป็นการกำหนดให้ข้อมูลในคอลัมน์นั้นเป็น null ได้หรือไม่
    • unique เป็นการกำหนดให้ในคอลัมน์นั้น มีค่าในแต่ละแถวที่ไม่เหมือนกัน
    • default: some_value เป็นการกำหนดค่าเริ่มต้น
    • increment เป็นการกำหนดให้คอลัมน์นั้นเป็น Auto increment

การกำหนดค่า default ในแต่ละคอลัมน์ขึ้นกับชนิดของข้อมูลในคอลัมน์ โดย

  1. กรณีที่เป็น number value เรากำหนดให้มีค่าเป็น 123 หรือ 123.456
  2. กรณีที่เป็น string เรากำหนดให้เป็นข้อความ ‘xxx’
  3. กรณีที่เป็นกำหนดข้อมูลด้วยการใช้ฟังก์ชัน (expression value) ตัวอย่างเช่นชนิดข้อมูลที่เป็น timestamp ที่เราต้องการเรียกค่าในเวลานั้น ๆ เราทำได้โดยการเขียนค่า default ได้โดยใส่เครื่องหมาย ` หน้าหลัง แล้วข้างในเขียนด้วยฟังก์ชัน now()
  4. นอกจากนี้ กรณีที่ข้อมูลเป็น boolean เรากำหนดให้ค่า default เป็น true, false หรือ null

นอกจากนี้ เรายังกำหนดชื่อที่ใช้เรียกแทน Table นั้น ๆ ได้โดยเพิ่ม as < ชื่อที่ต้องการให้เรียก > ตามหลังชื่อ table_name ได้

Relationship & Foreign Key

ถัดจากนั้น เรากำหนด Relation ระหว่าง Table ได้โดยใส่ Ref ใน column_setting ของแต่ละคอลัมน์ใน Table (inline-form) หรือใส่ Ref แยกออกมา (เช่น short-form หรือ long-form) ตัวอย่างแสดงได้ตามด้านล่างนี้

Table table1 {
    id integer
    column1 integer
}

// Inline-form
Table table2 {
    id integer
    colmn2 integer [ref: > table1.column1]
}

//Long form
Ref name_optional {
  table1.column1 < table2.column2
}

//Short form:
Ref name_optional: table1.column1 < table2.column2

โดยเรากำหนด Relationship ได้โดยการกำหนดด้วยหลัก Cardinality Ratio ได้แก่

  1. คือ one-to-one
  2. < คือ one-to-many
  3. > คือ many-to-one
  4. <> คือ many-to-many

เราสามารถกำหนดชื่อคอลัมน์ได้มากกว่า 1 คอลัมน์ได้โดยการกำหนดตามตัวอย่างด้านล่างนี้

Ref: merchant_periods.(merchant_id, country_code) > merchants.(id, country_code)

นอกจากนี้ เราสามารถตั้งค่าให้กับแต่ละ Relationship ได้อีก [9] โดยตั้งค่าได้ทั้ง short-form และ long-form แต่ inline-form ส่วนนี้ไม่รองรับ โดยเราตั้งค่าได้โดยการพิมพ์ตามด้านล่างนี้หลัง Relationship

Ref: Table1.column1 < Table2.column2 [setting1: setting2]

โดยการตั้งค่าเราสามารถตั้งค่าตรง setting1 ให้เป็น delete หรือ update ใช้ในกรณีเมื่อมีการลบ หรือมีการอัพเดทของข้อมูล เราจะสั่งให้ทำตาม setting2 ได้โดย

  • cascade ใช้ในกรณีที่มีการลบ หรือมีการอัพเดทข้อมูลในตารางที่มี Primary Key ข้อมูลในตารางที่มี Foreign Key จะถูกลบหรือแก้ไขทั้งหมด (โดยเขียนใน Database ได้เป็น CASCADE)
  • restrict กรณีที่ตารางที่มี Foreign Key ยังมีข้อมูลอยู่ เราจะลบ หรือแก้ไขข้อมูลในตารางที่มี Primary Key ไม่ได้ (โดยเขียนใน Database ได้เป็น RESTRICT)
  • set default กรณีที่ลบ หรือมีการแก้ไขข้อมูลในตารางที่มี Primary Key จะทำให้ข้อมูลในตารางที่มี Foreign Key จะเก็บค่าเริ่มต้น (Default) โดยในคอลัมน์ที่มี Foreign Key นั้นจะต้องกำหนดค่า Default ไว้ (โดยเขียนใน Database ได้เป็น SET DEFAULT)
  • null กรณีที่ลบ หรือมีการแก้ไขข้อมูลในตารางที่มี Primary Key จะทำให้ข้อมูลในตารางที่มี Foreign Key จะเก็บค่า null ไว้ อย่างไรก็ดีคอลัมน์ที่เป็น Foreign Key จะต้องเก็บค่า null ได้ (โดยเขียนใน Database ได้เป็น SET NULL)
  • no action ไม่ว่าข้อมูลในตารางที่มี Primary Key จะถูกลบหรือแก้ไข ข้อมูลในตารางที่มี Foreign Key จะไม่เปลี่ยนแปลง (โดยเขียนใน Database ได้เป็น NO ACTION)

Comment

เราสามารถใส่คอมเม้นต์ใน DBML ด้วยบรรทัดเดียว หรือหลายบรรทัดได้โดยการเขียนโค้ดตามด้านล่างนี้

// singe line comment

/*
   multiple line comments
*/

Enum

เป็นการกำหนดค่าในคอลัมน์นั้น ๆ ให้เป็นไปตามค่าที่กำหนดในรายการ โดยชื่อของ Enum สามารถใส่เป็นชนิดของคอลัมน์ใน Table ได้เลย โดยตัวอย่างแสดงตามด้านล่างนี้

Enum job_status {
    created [note: 'Waiting to be processed']
    running
    done
    failure
}

Table jobs {
    id integer [primary key, increment]
    status job_status
}

Note

การใส่ Note เราสามารถใส่ได้หลายที่ ได้แก่ ใส่ไว้ข้างใน Project กับ Note กับใส่ไว้ข้างหลังแต่ละคอลัมน์ใน Table ตรง column_setting และใส่ไว้ข้างหลังของค่าใน Enum โดยแสดงตัวอย่างตามด้านล่างนี้

Project Notes
Project DBML {
    Note: '''

    '''
}

Table Notes
Table users {
    id int [pk]
    name varchar
    Note: 'Stores user data'
}

Column Notes
column_name column_type [note: 'column notes']

Enum Notes
enum job_status {
    created [note: 'Waiting to be processed']
    running
    done
    failure
}

โดยเครื่องหมาย

  • ”’ กำหนดไว้ว่า Note ตรงนั้นสามารถเขียนได้หลายบรรทัด
  • < Enter > เป็นการกำหนด Line Break ของข้อความให้ตัดขึ้นบรรทัดใหม่
  • \ เป็นการกำหนด Line Continue เพื่อให้เราสามารถเคาะ Enter เพื่อขึ้นบรรทัดใหม่ได้โดยข้อความที่แสดงบนหน้าจอยังคงเป็นข้อความบรรทัดเดียวกันอยู่
  • \\ เป็นการกำหนด Escape Character ด้วยเครื่องหมาย \
  • \”’ เป็นการกำหนด Escape Character ด้วยเครื่องหมาย ”’

Table Group

เราสามารถจัดกลุ่มตารางให้อยู่ในหมวดเดียวกันได้โดยการกำหนด TableGroup ตามด้านล่างนี้

TableGroup tablegroup_name {
    table1
    table2
    table3
}

ข้อแนะนำ

เมื่อออกแบบตารางเสร็จแล้ว ให้นำไฟล์เก็บไว้ใน Root folder แบบเดียวกันกับไฟล์ package.json หรือ README.md

ตัวอย่างการใช้งาน DBML

ตัวอย่างนี้เอามาจากงานที่เคยทำ Conference The 3rd ASEAN – USC Workshop on Informatics and Engineering for SDGs ตอนเรียนป.โทในหัวข้อ A Development of a Web-based Cervical Range of Motion Estimation System for Video Analysis

งานนี้เป็นงานที่ทำเว็บทั้ง Backend และ Frontend สำหรับการนำโมเดล Machine Learning อย่าง Object Detection, Face Detection, Head Pose Estimation และ Semantic Segmentation สำหรับการวัดการเคลื่อนไหวศีรษะและลำคอ (Cervical Range of Motion) และการวัดการอ้าปาก (Mouth Opening Measurement) ในผู้ป่วยที่มีปัญหาด้านการกลืน

โดยเราจะนำส่วนของ Database ที่เราเคยออกแบบในแต่ละ Table มาเขียนโดยการใช้ DBML เพื่อทำ Entity Relationship Diagram (ER Diagram) ส่วนข้อมูลในตารางอันนี้ไม่ได้นำออกมาเนื่องมาจากติดเรื่องข้อมูลของผู้ป่วยตาม Ethics ครับ

การออกแบบทำได้โดยการเขียนโค้ดตามด้านล่างนี้ โดยแบ่งเป็น 3 ส่วน ได้แก่

  • Project
  • Table
  • และ Relation

Project

เรากำหนดรายละเอียด Project สำหรับการออกแบบ Database ที่ใช้งานกับ MySQL ได้ตามด้านล่างนี้

Project FACE_MOVEMENT_DATA {
  database_type: 'MySQL'
  Note: 'The database used for rehabilitation assessment by using CROM measurement, and mouth-opening measurement.'  
}

Table

เรากำหนดรายละเอียด Table โดยแบ่งเป็น Table อย่างคร่าว ๆ ได้ทั้งหมด 5 Table ได้แก่

  • MEMBER_DATA สำหรับการทำระบบสมาชิกในเว็บไซต์ โดยแบ่งเป็นบุคลากรทางการแพทย์ กับผู้ดูแลระบบ
  • PATIENT_DATA เป็นการเก็บข้อมูล Subject ที่เข้ารับการตรวจในระบบ
  • RECORD_DATA สำหรับการเก็บรายการบันทึกวิดีโอในระบบสำหรับแต่ละ Subject
  • RECORD_VIDEO_DATA สำหรับการเก็บที่อยู่ไฟล์วิดีโอของแต่ละ Record
  • DETECT_DATA สำหรับการเก็บข้อมูลผลการวัดการเคลื่อนไหวศีรษะและลำคอ และผลการวัดการอ้าปาก

MEMBER_DATA

ตารางนี้ออกแบบมาสำหรับการทำระบบสมาชิกในเว็บไซต์ โดยแบ่งเป็นบุคลากรทางการแพทย์ (user) กับผู้ดูแลระบบ (admin) โดยออกแบบให้มีคอลัมน์ตามด้านล่างนี้

  • id เป็นการเก็บข้อมูล User ID โดยกำหนดให้เป็น Primary Key และค่าจะเพิ่มทุกครั้งที่เพิ่มข้อมูลเข้ามาในระบบ
  • email เป็นการเก็บข้อมูล E-Mail โดยกำหนดไม่ให้มีค่าว่าง
  • password เป็นการเก็บข้อมูล Password โดยกำหนดไม่ให้มีค่าว่าง และการเก็บข้อมูลนี้จะเก็บแบบเข้ารหัสไว้ ไม่เก็บ Plain Text
  • name และ surname เป็นการเก็บข้อมูลชื่อและนามสกุล โดยกำหนดไม่ให้มีค่าว่างเช่นกัน
  • user_type เป็นการเก็บข้อมูลประเภทผู้ใช้ โดย 0 – user และ 1 – admin
Table FACE_MOVEMENT_DATA.MEMBER_DATA {
  id integer [primary key, increment]
  email text [not null]
  password text [not null]
  name text [not null]
  surname text [not null]
  user_type int [default: 0, Note: 'Type 0: user, and 1: admin'] 
  Note: 'The list of members in the rehabilitation assessment system.'
}

PATIENT_DATA

ตารางนี้ออกแบบมาเพื่อเก็บข้อมูล Subject ที่เข้ารับการตรวจในระบบ โดยออกแบบให้มีคอลัมน์ตามด้านล่างนี้

  • id เป็นการเก็บข้อมูล Patient ID โดยกำหนดให้เป็น Primary Key และค่าจะเพิ่มทุกครั้งที่เพิ่มข้อมูลเข้ามาในระบบ
  • author_id เป็นการเก็บข้อมูล User ID ที่เพิ่มข้อมูล Subject นี้ โดยกำหนดให้เป็น Foreign Key กับ id ใน MEMBER_DATA และกำหนดไม่ให้มีค่าว่าง
  • HN, name, surname เป็นรายละเอียดเลขที่ ชื่อ นามสกุล Subject โดยกำหนดไม่ให้มีค่าว่าง
Table FACE_MOVEMENT_DATA.PATIENT_DATA {
  id integer [primary key, increment]
  author_id integer
  HN varchar(15) [not null]
  name varchar(255) [not null]
  surname varchar(255) [not null]

  Note: 'The list of the patients in the rehabilitation assessment system.'
}

RECORD_DATA

ตารางนี้ออกแบบมาเพื่อเก็บรายการบันทึกวิดีโอในระบบสำหรับแต่ละ Subject โดยออกแบบให้มีคอลัมน์ตามด้านล่างนี้

  • id เป็นการเก็บข้อมูล Record ID โดยกำหนดให้เป็น Primary Key และค่าจะเพิ่มทุกครั้งที่เพิ่มข้อมูลเข้ามาในระบบ
  • author_id เป็นการเก็บข้อมูล User ID ที่เพิ่มข้อมูล Record นี้ โดยกำหนดให้เป็น Foreign Key กับ id ใน MEMBER_DATA และกำหนดไม่ให้มีค่าว่าง
  • patient_id เป็นการเก็บข้อมูล Patient ID โดยกำหนดให้เป็น Foreign Key กับ id ใน PATIENT_DATA และกำหนดไม่ให้มีค่าว่าง
  • record_datetime เป็นการกำหนดวันและเวลาของการเพิ่มรายการบันทึกวิดีโอ โดยกำหนดให้เป็น timestamp ด้วยค่าเริ่มต้นว่า now()
  • description_text เป็นการใส่คำอธิบายของการบันทึกวิดีโอ โดยกำหนดให้มีค่าว่างได้
Table FACE_MOVEMENT_DATA.RECORD_DATA {
  id integer [primary key, increment]
  author_id integer [not null]
  patient_id integer [not null]
  reocord_datetime timestamp [default: `now()`]
  description text [null]
  Note: 'The list of the recorded video for each patient.'
}

RECORD_VIDEO_DATA

ตารางนี้ออกแบบมาเพื่อเก็บที่อยู่ไฟล์วิดีโอของแต่ละ Record โดยออกแบบให้มีคอลัมน์ตามด้านล่างนี้

  • id เป็นการเก็บข้อมูล Video ID โดยกำหนดให้เป็น Primary Key และค่าจะเพิ่มทุกครั้งที่เพิ่มข้อมูลเข้ามาในระบบ
  • record_id เป็นการเก็บข้อมูล Record ID โดยกำหนดให้เป็น Foreign Key กับ id ใน RECORD_DATA
  • video_name เป็นการกำหนดชื่อวิดีโอ โดยกำหนดไม่ให้มีค่าว่าง
  • path เป็นการกำหนดที่อยู่ไฟล์ โดยกำหนดไม่ให้มีค่าว่าง
  • view_type เป็นการกำหนดมุมของกล้อง ถ่ายจากด้านหน้า (front) หรือด้านข้าง (side)
Table FACE_MOVEMENT_DATA.RECORD_VIDEO_DATA {
  id integer [primary key, increment]
  record_id integer [not null]
  video_name text [not null]
  path text [not null]
  view_type text [not null, default: 'front']
  Note: 'The list of the videos in each record for each patient.'
}

DETECT_DATA

ตารางนี้เก็บข้อมูลผลการวัดการเคลื่อนไหวศีรษะและลำคอ และผลการวัดการอ้าปาก โดยออกแบบให้มีคอลัมน์ตามด้านล่างนี้

  • id เป็นการเก็บข้อมูล Detection ID โดยกำหนดให้เป็น Primary Key และค่าจะเพิ่มทุกครั้งที่เพิ่มข้อมูลเข้ามาในระบบ
  • record_id เป็นการเก็บข้อมูล Record ID โดยกำหนดให้เป็น Foreign Key กับ id ใน RECORD_DATA และกำหนดไม่ให้มีค่าว่าง
  • detect_type เป็นการกำหนดชนิดของการตรวจวัด โดยในที่นี่แบ่งเป็นการวัดการเคลื่อนไหวศีรษะและลำคอที่วัดทั้ง 6 ทิศทาง (Flexion, Extension, Left Lateral Bending, Right Lateral Bending, Left Rotation และ Right Rotation) และวัดการอ้าปาก (Mouth Opening) ร่วมกับกำหนดไม่ให้มีค่าว่าง
  • image_path เป็นการกำหนดที่อยู่ไฟล์ภาพของการวัดการเคลื่อนไหว โดยกำหนดไม่ให้มีค่าว่าง
  • datetime เป็นวันและเวลาที่ตรวจวัด โดยกำหนดไม่ให้มีค่าว่าง ร่วมกับกำหนดค่าเริ่มต้นโดยใช้ฟังก์ชัน now()
  • video_time เป็นการกำหนดเวลาของไฟล์วิดีโอว่าบันทึกทีช่วงเวลาไหน โดยกำหนดไม่ให้มีค่าว่าง
  • detection_data เป็นผลการวัดการเคลื่อนไหว โดยกำหนดให้มีค่าว่างได้
Table FACE_MOVEMENT_DATA.DETECT_DATA {
  id integer [primary key, increment]
  record_id integer [not null]
  detect_type varchar(255) [not null]
  image_path text [null]
  detection_data text [null]
  datetime timestamp [not null, default: `now()`]
  video_time integer [not null]
  Note: 'The list of the detection results for each recorded video for each patient.'
}

Relation

การกำหนด Relationship นี้ เรากำหนดให้เป็น One to Many โดยกำหนดให้มี Relationship ของ

  • MEMBER_DATA.id
  • PATIENT_DATA.id
  • RECORD_DATA.id

โดยรายละเอียดของ Relationship ที่เป็น Primary Key และ Foreign Key เราได้แสดงในขั้นตอนที่แล้วที่เป็นการออกแบบ Table แล้ว

ส่วนการตั้งค่า Relationship ทุกอัน เรากำหนดให้เป็น DELETE CASCADE ก็คือ เมื่อเราลบข้อมูลพวกนี้ในตารางที่มี Primary Key แล้ว ข้อมูลที่มี Foreign Key ก็จะโดนลบไปด้วย

ส่วนโค้ด DBML ที่แสดงรายละเอียดของ Relationship เราเขียนไว้ตามด้านล่างนี้

ความสัมพันธ์ของ MEMBER_DATA.id

// One-to-many by using author_id to get list of patients, and records. 
Ref: FACE_MOVEMENT_DATA.MEMBER_DATA.id < FACE_MOVEMENT_DATA.PATIENT_DATA.author_id [delete: cascade]
Ref: FACE_MOVEMENT_DATA.MEMBER_DATA.id < FACE_MOVEMENT_DATA.RECORD_DATA.author_id [delete: cascade]

ความสัมพันธ์ของ PATIENT_DATA.id

// One-to-many by using patient_id to get list of records.
Ref: FACE_MOVEMENT_DATA.PATIENT_DATA.id < FACE_MOVEMENT_DATA.RECORD_DATA.patient_id [delete: cascade]

// One-to-many by using patient_id to get list of records.
Ref: FACE_MOVEMENT_DATA.PATIENT_DATA.id < FACE_MOVEMENT_DATA.RECORD_DATA.patient_id [delete: cascade]

ความสัมพันธ์ของ RECORD_DATA.id

// One-to-many by using record_id to get the list of videos.
Ref: FACE_MOVEMENT_DATA.RECORD_DATA.id < FACE_MOVEMENT_DATA.RECORD_VIDEO_DATA.record_id [delete: cascade]

// One-to-many by using record_id to get the list of detection results
Ref: FACE_MOVEMENT_DATA.RECORD_DATA.id < FACE_MOVEMENT_DATA.DETECT_DATA.record_id [delete: cascade]

ER Diagram

โดยแสดงออกมาเป็น ER Diagram ตามด้านล่างนี้

ที่มา

  1. https://www.mindphp.com/%E0%B8%84%E0%B8%B9%E0%B9%88%E0%B8%A1%E0%B8%B7%E0%B8%AD/73-%E0%B8%84%E0%B8%B7%E0%B8%AD%E0%B8%AD%E0%B8%B0%E0%B9%84%E0%B8%A3/6048-entity-relations-diagram-erd.html
  2. http://www.geog.pn.psu.ac.th/ComGeo60/ER%20diagram.pdf
  3. https://nickuntitled.com/2024/01/31/14-get-database-data-to-show-dashboard/
  4. http://std.bus.tu.ac.th/web04/images/DBMS/dbms6.pdf
  5. https://blog.clicknext.com/what-is-er-diagram/
  6. https://www2.cs.science.cmu.ac.th/courses/204222/lib/exe/fetch.php?media=ch07_er_model.pdf
  7. https://vertabelo.com/blog/conceptual-logical-physical-data-model/
  8. https://dbml.dbdiagram.io/home/
  9. https://sattayametharakcheep.medium.com/%E0%B9%80%E0%B8%82%E0%B8%B5%E0%B8%A2%E0%B8%99%E0%B9%80%E0%B8%A7%E0%B9%87%E0%B8%9A-%E0%B9%80%E0%B8%94%E0%B8%AD%E0%B8%B0-%E0%B8%8B%E0%B8%B5%E0%B8%A3%E0%B8%B5%E0%B8%AA%E0%B9%8C-%E0%B8%95%E0%B8%AD%E0%B8%99%E0%B8%97%E0%B8%B5%E0%B9%88-34-%E0%B8%81%E0%B8%B2%E0%B8%A3%E0%B8%81%E0%B8%B3%E0%B8%AB%E0%B8%99%E0%B8%94%E0%B9%80%E0%B8%87%E0%B8%B7%E0%B9%88%E0%B8%AD%E0%B8%99%E0%B9%84%E0%B8%82-constraint-%E0%B9%80%E0%B8%9E%E0%B8%B7%E0%B9%88%E0%B8%AD%E0%B9%80%E0%B8%8A%E0%B8%B7%E0%B9%88%E0%B8%AD%E0%B8%A1%E0%B9%82%E0%B8%A2%E0%B8%87%E0%B8%82%E0%B9%89%E0%B8%AD%E0%B8%A1%E0%B8%B9%E0%B8%A5%E0%B8%A3%E0%B8%B0%E0%B8%AB%E0%B8%A7%E0%B9%88%E0%B8%B2%E0%B8%87%E0%B8%95%E0%B8%B2%E0%B8%A3%E0%B8%B2%E0%B8%87-cd9e4796c214

By Kittisak Chotikkakamthorn

อดีตนักศึกษาฝึกงานทางด้าน AI ที่ภาควิชาวิศวกรรมไฟฟ้า มหาวิทยาลัย National Chung Cheng ที่ไต้หวัน ที่กำลังหางานทางด้าน Data Engineer ที่มีความสนใจทางด้าน Data, Coding และ Blogging / ติดต่อได้ที่: contact [at] nickuntitled.com