แผนภาพ ER
แผนภาพความสัมพันธ์เชิงเอนทิตี (เรียกสั้น ๆ ว่าแผนภาพ ER) อธิบายสิ่งต่าง ๆ ที่น่าสนใจซึ่งมีความสัมพันธ์กันในแวดวงความรู้เฉพาะ แผนภาพ ER พื้นฐานประกอบด้วยประเภทเอนทิตี (ใช้จัดหมวดหมู่สิ่งต่าง ๆ ที่น่าสนใจ) และระบุความสัมพันธ์ที่สามารถมีได้ระหว่างเอนทิตี (อินสแตนซ์ของประเภทเอนทิตีเหล่านี้),โปรดทราบว่าในการใช้งานจริงของแผนภาพ ER เกือบทุกครั้งจะเรียก 'ประเภทเอนทิตี' สั้น ๆ ว่า 'เอนทิตี' ตัวอย่างเช่น 'ประเภทเอนทิตี CUSTOMER' มักเรียกตรง ๆ ว่า 'เอนทิตี CUSTOMER' การปฏิบัติเช่นนี้พบได้ทั่วไปมาก จึงไม่แนะนำให้ใช้วิธีอื่น แต่ในทางเทคนิคแล้ว เอนทิตีคืออินสแตนซ์เชิงนามธรรมของประเภทเอนทิตี และแผนภาพ ER แสดงอินสแตนซ์เชิงนามธรรมเหล่านี้และความสัมพันธ์ระหว่างกัน นี่คือเหตุผลว่าทำไมเอนทิตีจึงตั้งชื่อด้วยคำนามเอกพจน์เสมอ
คัดลอก
ฝัง
---
title: Order example
---
erDiagram
CUSTOMER ||--o{ ORDER : places
ORDER ||--|{ LINE-ITEM : contains
CUSTOMER }|..|{ DELIVERY-ADDRESS : uses
ชื่อเอนทิตีมักเป็นตัวพิมพ์ใหญ่ แม้ว่าจะไม่มีมาตรฐานที่เป็นที่ยอมรับในเรื่องนี้ และไวยากรณ์ของ Mermaid ก็ไม่ได้บังคับ
ความสัมพันธ์ระหว่างเอนทิตี แสดงด้วยเส้นที่มีเครื่องหมายที่ปลายซึ่งระบุคาร์ดินาลิตี Mermaid ใช้สัญลักษณ์เท้ารวมซึ่งเป็นที่นิยมที่สุด ซึ่งแสดงอย่างชัดเจนว่าเอนทิตีที่มันเชื่อมต่ออยู่อาจมีอินสแตนซ์ได้หลายตัว
แผนภาพ ER ใช้ได้หลากหลายวัตถุประสงค์ ตั้งแต่แบบจำลองเชิงตรรกะเชิงนามธรรมที่ไม่มีการรายละเอียดการนำไปใช้ใด ๆ ไปจนถึงแบบจำลองทางกายภาพของตารางฐานข้อมูลเชิงสัมพันธ์ การใส่คำจำกัดความของคุณสมบัติในแผนภาพ ER ช่วยให้เข้าใจจุดประสงค์และความหมายของเอนทิตี คำจำกัดความของคุณสมบัติเหล่านี้ไม่จำเป็นต้องครบถ้วนสมบูรณ์ โดยทั่วไปคุณสมบัติเพียงส่วนเล็ก ๆ ก็เพียงพอแล้ว Mermaid อนุญาตให้กำหนดคุณสมบัติเหล่านี้ด้วยประเภทและชื่อได้
คัดลอก
ฝัง
erDiagram
CUSTOMER ||--o{ ORDER : places
CUSTOMER {
string name
string custNumber
string sector
}
ORDER ||--|{ LINE-ITEM : contains
ORDER {
int orderNumber
string deliveryAddress
}
LINE-ITEM {
string productCode
int quantity
float pricePerUnit
}
เมื่อใส่คุณสมบัติในแผนภาพ ER คุณต้องตัดสินใจว่าจะรวมคีย์นอกเป็นคุณสมบัติหรือไม่ ซึ่งอาจขึ้นอยู่กับว่าคุณต้องการแสดงโครงสร้างตารางเชิงสัมพันธ์มากเพียงใด หากแผนภาพของคุณเป็นแบบจำลองเชิงตรรกะและไม่ได้ตั้งใจจะสื่อถึงการนำไปใช้เชิงสัมพันธ์ ก็ไม่ควรใส่คุณสมบัติเหล่านี้ เนื่องจากความสัมพันธ์ได้สื่อถึงวิธีการเชื่อมโยงระหว่างเอนทิตีแล้ว ตัวอย่างเช่น โครงสร้างข้อมูล JSON สามารถใช้ array เพื่อสร้างความสัมพันธ์แบบหนึ่งต่อหลายโดยไม่มีคุณสมบัติคีย์นอกได้ ในทำนองเดียวกัน ภาษาโปรแกรมเชิงวัตถุสามารถใช้พอยน์เตอร์หรือการอ้างอิงไปยังคอลเลกชันได้ แม้กระทั่งสำหรับแบบจำลองที่มุ่งนำไปใช้เชิงสัมพันธ์ ก็อาจเห็นได้ว่าการใส่คุณสมบัติคีย์นอกเป็นการแสดงข้อมูลที่ความสัมพันธ์ได้แสดงไปแล้วซ้ำ และไม่ได้เพิ่มความหมายให้กับเอนทิตี
ไวยากรณ์
เอนทิตีและความสัมพันธ์
ไวยากรณ์ Mermaid ของแผนภาพ ER เข้ากันได้กับ PlantUML และได้ขยายความสามารถสำหรับการติดป้ายกำกับความสัมพันธ์ แต่ละคำสั่งประกอบด้วยสิ่งต่อไปนี้:
คัดลอก
ฝัง
<first-entity> [<relationship> <second-entity> : <relationship-label>]
- first-entity เป็นชื่อของเอนทิตีแรก ชื่อรองรับอักขระ Unicode ใดก็ได้ และหากล้อมด้วยเครื่องหมายอัญประกาศคู่ ก็สามารถมีช่องว่างได้
- relationship อธิบายความสัมพันธ์ระหว่างสองเอนทิตี
- second-entity เป็นชื่อของอีกเอนทิตีหนึ่ง
- relationship-label อธิบายความสัมพันธ์โดยมองจากมุมของเอนทิตีแรก
ตัวอย่าง:
คัดลอก
ฝัง
PROPERTY ||--|{ ROOM : contains
คำสั่งนี้สามารถเข้าใจได้ว่า: คุณสมบัติหนึ่งประกอบด้วยห้องหนึ่งห้องหรือมากกว่า ส่วนห้องหนึ่งห้องเป็นของทรัพย์สินเพียงหนึ่งเดียว ป้ายกำกับที่นี่มองจากมุมของเอนทิตีแรก: ทรัพย์สินหนึ่งประกอบด้วยห้องหนึ่งห้อง แต่ห้องหนึ่งห้องไม่ได้ประกอบด้วยทรัพย์สิน เมื่อมองจากมุมของเอนทิตีที่สอง โดยทั่วไปป้ายกำกับที่เทียบเท่ากันสามารถอนุมานได้ง่าย
มีเพียงส่วน first-entity เท่านั้นที่บังคับ ซึ่งทำให้สามารถแสดงเอนทิตีที่ไม่มีความสัมพันธ์ได้ ซึ่งมีประโยชน์มากเมื่อสร้างแผนภาพแบบทำซ้ำ หากระบุส่วนอื่นของคำสั่ง ส่วนทั้งหมดจะต้องกรอก
ข้อความ Unicode
ชื่อเอนทิตี ความสัมพันธ์ และคุณสมบัติ รองรับข้อความ unicode ทั้งหมด
คัดลอก
ฝัง
erDiagram
"This ❤ Unicode"
ไวยากรณ์ของความสัมพันธ์
ส่วนความสัมพันธ์ของแต่ละคำสั่งสามารถแยกออกเป็นสามส่วนย่อยได้:
- คาร์ดินาลิตีของเอนทิตีแรกเมื่อเทียบกับเอนทิตีที่สอง
- ความสัมพันธ์นี้ให้การระบุเอกลักษณ์แก่เอนทิตีย่อยหรือไม่
- คาร์ดินาลิตีของเอนทิตีที่สองเมื่อเทียบกับเอนทิตีแรก
คาร์ดินาลิตีเป็นคุณสมบัติที่ใช้อธิบายจำนวนองค์ประกอบที่อีกเอนทิตีหนึ่งสามารถเชื่อมโยงกับเอนทิตีที่เกี่ยวข้องได้ ในตัวอย่างด้านบน PROPERTY หนึ่งสามารถเชื่อมโยงกับอินสแตนซ์ ROOM หนึ่งตัวหรือมากกว่า ส่วน ROOM หนึ่งตัวสามารถเชื่อมโยงกับ PROPERTY ได้เพียงหนึ่งตัว เครื่องหมายคาร์ดินาลิตีแต่ละอันมีสองอักขระ อักขระชั้นนอกสุดแทนค่ามากสุด อักขระชั้นในสุดแทนค่าน้อยสุด ตารางด้านล่างสรุปความเป็นไปได้ของคาร์ดินาลิตี:
| ค่า (ซ้าย) | ค่า (ขวา) | ความหมาย |
|---|---|---|
| |o | o| | ศูนย์หรือหนึ่ง |
| || | || | หนึ่งพอดี |
| }o | o{ | ศูนย์หรือหลาย (ไม่มีขีดจำกัดบน) |
| }| | |{ | หนึ่งหรือหลาย (ไม่มีขีดจำกัดบน) |
| ค่า (ซ้าย) | ค่า (ขวา) | ความหมาย |
|---|---|---|
| one or zero | one or zero | ศูนย์หรือหนึ่ง |
| zero or one | zero or one | ศูนย์หรือหนึ่ง |
| one or more | one or more | หนึ่งหรือหลาย |
| one or many | one or many | หนึ่งหรือหลาย |
| many(1) | many(1) | หนึ่งหรือหลาย |
| 1+ | 1+ | หนึ่งหรือหลาย |
| zero or more | zero or more | ศูนย์หรือหลาย |
| zero or many | zero or many | ศูนย์หรือหลาย |
| many(0) | many(0) | ศูนย์หรือหลาย |
| 0+ | 0+ | ศูนย์หรือหลาย |
| only one | only one | หนึ่งพอดี |
| 1 | 1 | หนึ่งพอดี |
การระบุเอกลักษณ์
ความสัมพันธ์สามารถแบ่งได้เป็นแบบระบุเอกลักษณ์และแบบไม่ระบุเอกลักษณ์ โดยเรนเดอร์เป็นเส้นทึบหรือเส้นประตามลำดับ สิ่งนี้สำคัญเมื่อเอนทิตีหนึ่งไม่สามารถดำรงอยู่ได้อย่างอิสระหากไม่มีอีกเอนทิตีหนึ่ง ตัวอย่างเช่น บริษัทที่ให้ประกันสำหรับการขับรถของบุคคลอาจต้องจัดเก็บข้อมูล NAMED-DRIVER เมื่อสร้างแบบจำลอง เราอาจสังเกตก่อนว่า CAR หนึ่งคันสามารถขับได้โดย PERSON หลายคน และ PERSON หนึ่งคนสามารถขับ CAR ได้หลายคัน ทั้งสองสามารถดำรงอยู่ได้โดยไม่ต้องมีอีกฝ่าย ดังนั้นนี่จึงเป็นความสัมพันธ์แบบไม่ระบุเอกลักษณ์ ซึ่งเราสามารถระบุใน Mermaid ได้เป็น: PERSON }|..|{ CAR : "driver" โปรดสังเกตจุดสองจุดตรงกลางความสัมพันธ์ ซึ่งจะทำให้วาดเส้นประระหว่างสองเอนทิตี อย่างไรก็ตาม เมื่อความสัมพันธ์แบบหลายต่อหลายนี้แยกออกเป็นความสัมพันธ์แบบหนึ่งต่อหลายสองความสัมพันธ์ เราสังเกตว่า NAMED-DRIVER ไม่สามารถดำรงอยู่ได้หากไม่มี PERSON และ CAR ความสัมพันธ์เหล่านี้กลายเป็นความสัมพันธ์แบบระบุเอกลักษณ์ และจะแสดงด้วยเครื่องหมายขีดกลาง ซึ่งก็คือเส้นทึบ:
| ค่า | นามแฝง |
|---|---|
| -- | การระบุ |
| .. | ไม่ระบุ |
| ค่า | 别名为 |
|---|---|
| to | การระบุ |
| optionally to | ไม่ระบุ |
คัดลอก
ฝัง
erDiagram
CAR ||--o{ NAMED-DRIVER : allows
PERSON }o..o{ NAMED-DRIVER : is
คัดลอก
ฝัง
erDiagram
CAR ||--o{ NAMED-DRIVER : allows
PERSON }o--o{ NAMED-DRIVER : is
คุณสมบัติ
สามารถกำหนดคุณสมบัติให้กับเอนทิตีได้โดยระบุชื่อเอนทิตี ตามด้วยบล็อกที่มี type name หลายรายการ โดยบล็อกถูกล้อมด้วยวงเล็บปีกกา {} คุณสมบัติจะแสดงภายในกล่องเอนทิตี ตัวอย่าง:
คัดลอก
ฝัง
erDiagram
CAR ||--o{ NAMED-DRIVER : allows
CAR {
string registrationNumber
string make
string model
}
PERSON ||--o{ NAMED-DRIVER : is
PERSON {
string firstName
string lastName
int age
}
ค่า type ต้องขึ้นต้นด้วยอักขระตัวอักษร และสามารถมีตัวเลข ขีดกลาง ขีดล่าง วงเล็บเล็ก และวงเล็บเหลี่ยมได้ ค่า name มีรูปแบบคล้ายกับ type แต่สามารถขึ้นต้นด้วยเครื่องหมายดอกจันได้เช่นกัน ซึ่งเป็นอีกทางเลือกหนึ่งในการบ่งชี้ว่าคุณสมบัติเป็นคีย์หลัก นอกเหนือจากนี้ไม่มีข้อจำกัดอื่น และไม่มีชุดประเภทข้อมูลที่ถูกต้องโดยนัย
นามแฝงของเอนทิตี
สามารถใช้วงเล็บเหลี่ยมเพื่อเพิ่มนามแฝงให้กับเอนทิตีได้ หากมีนามแฝง แผนภาพจะแสดงนามแฝงแทนชื่อเอนทิตี โดยนามแฝงปฏิบัติตามกฎทั้งหมดเช่นเดียวกับชื่อเอนทิตี
คัดลอก
ฝัง
erDiagram
p[Person] {
string firstName
string lastName
}
a["Customer Account"] {
string email
}
p ||--o| a : has
คีย์และความคิดเห็นของคุณสมบัติ
คุณสมบัติยังสามารถกำหนดคีย์หรือความคิดเห็นได้ คีย์อาจเป็นคีย์หลัก (PK) คีย์นอก (FK) หรือคีย์เฉพาะ (UK) หากต้องการระบุข้อจำกัดคีย์หลายรายการบนคุณสมบัติเดียว ให้ใช้จุลภาคแยก (เช่น PK, FK) ความคิดเห็นถูกกำหนดโดยเครื่องหมายอัญประกาศคู่ที่ส่วนท้ายของคุณสมบัติ และความคิดเห็นไม่สามารถมีอักขระเครื่องหมายอัญประกาศคู่ได้
คัดลอก
ฝัง
erDiagram
CAR ||--o{ NAMED-DRIVER : allows
CAR {
string registrationNumber PK
string make
string model
string[] parts
}
PERSON ||--o{ NAMED-DRIVER : is
PERSON {
string driversLicense PK "The license #"
string(99) firstName "Only 99 characters are allowed"
string lastName
string phone UK
int age
}
NAMED-DRIVER {
string carRegistrationNumber PK, FK
string driverLicence PK, FK
}
MANUFACTURER ||--o{ CAR : makes
ทิศทาง
คำสั่งทิศทางประกาศทิศทางของแผนภาพ
TB ประกาศว่าทิศทางของแผนภาพคือจากบนลงล่าง ส่วน BT สามารถกลับทิศทางเป็นจากล่างขึ้นบนได้
คัดลอก
ฝัง
erDiagram
direction TB
CUSTOMER ||--o{ ORDER : places
CUSTOMER {
string name
string custNumber
string sector
}
ORDER ||--|{ LINE-ITEM : contains
ORDER {
int orderNumber
string deliveryAddress
}
LINE-ITEM {
string productCode
int quantity
float pricePerUnit
}
LR ประกาศว่าทิศทางของแผนภาพคือจากซ้ายไปขวา ส่วน RL สามารถกลับทิศทางเป็นจากขวาไปซ้ายได้
คัดลอก
ฝัง
erDiagram
direction LR
CUSTOMER ||--o{ ORDER : places
CUSTOMER {
string name
string custNumber
string sector
}
ORDER ||--|{ LINE-ITEM : contains
ORDER {
int orderNumber
string deliveryAddress
}
LINE-ITEM {
string productCode
int quantity
float pricePerUnit
}
ตั้งค่าสไตล์ของโหนด
สามารถนำสไตล์เฉพาะไปใช้กับโหนดได้ เช่น ขอบที่หนาขึ้นหรือสีพื้นหลังที่ต่างออกไป
คัดลอก
ฝัง
erDiagram
id1||--||id2 : label
style id1 fill:pink,stroke:yellow,stroke-width:4px
style id2 fill:#69d,stroke:#f96,stroke-width:2px,color:#fff,stroke-dasharray: 5 5
ยังสามารถแนบสไตล์กับรายการโหนดในคำสั่งเดียวได้:
คัดลอก
ฝัง
style nodeId1,nodeId2 styleList
คลาส
สะดวกกว่าการกำหนดสไตล์ทุกครั้งคือการกำหนดคลาสสไตล์ แล้วแนบคลาสนั้นกับโหนดที่ต้องการรูปลักษณ์ที่ต่างออกไป
การกำหนดคลาสมีดังตัวอย่างต่อไปนี้:
คัดลอก
ฝัง
classDef className fill:pink,stroke:#333,stroke-width:4px
ยังสามารถกำหนดหลายคลาสในคำสั่งเดียวได้:
คัดลอก
ฝัง
classDef firstClassName,secondClassName font-size:12pt
ไวยากรณ์สำหรับแนบคลาสกับโหนดมีดังนี้:
คัดลอก
ฝัง
class nodeId1 className
ยังสามารถแนบคลาสหนึ่งกับรายการโหนดในคำสั่งเดียวได้:
คัดลอก
ฝัง
class nodeId1,nodeId2 className
ยังสามารถแนบหลายคลาสพร้อมกันได้:
คัดลอก
ฝัง
class nodeId1,nodeId2 className1,className2
วิธีที่สะดวกในการเพิ่มคลาสคือการใช้ตัวดำเนินการ ::: เพื่อแนบชื่อคลาสกับโหนด ดังที่แสดงด้านล่าง:
คัดลอก
ฝัง
erDiagram
direction TB
CAR:::someclass {
string registrationNumber
string make
string model
}
PERSON:::someclass {
string firstName
string lastName
int age
}
HOUSE:::someclass
classDef someclass fill:#f96
การประกาศความสัมพันธ์ระหว่างเอนทิตีสามารถใช้ไวยากรณ์ต่อไปนี้:
คัดลอก
ฝัง
erDiagram
CAR {
string registrationNumber
string make
string model
}
PERSON {
string firstName
string lastName
int age
}
PERSON:::foo ||--|| CAR : owns
PERSON o{--|| HOUSE:::bar : has
classDef foo stroke:#f00
classDef bar stroke:#0f0
classDef foobar stroke:#00f
คล้ายกับคำสั่ง class ไวยากรณ์แบบย่อก็สามารถนำหลายคลาสไปใช้ในคราวเดียวได้เช่นกัน:
คัดลอก
ฝัง
nodeId:::className1,className2
คลาสเริ่มต้น
หากคลาสใดมีชื่อว่า default มันจะถูกกำหนดให้กับโหนดทั้งหมดที่ไม่มีคำจำกัดความคลาสเฉพาะ
คัดลอก
ฝัง
classDef default fill:pink,stroke:#333,stroke-width:4px;
หมายเหตุ: สไตล์แบบกำหนดเองที่มาจากคำสั่ง style หรือคำสั่งคลาสอื่นมีลำดับความสำคัญสูงกว่า และจะเขียนทับสไตล์เริ่มต้น (ตัวอย่างเช่น คลาส default ตั้งค่าพื้นหลังสีชมพูให้กับโหนด แต่หากนำคลาส blue ไปใช้ จะให้สีพื้นหลังสีน้ำเงินกับโหนดนั้น)
คัดลอก
ฝัง
erDiagram
CAR {
string registrationNumber
string make
string model
}
PERSON {
string firstName
string lastName
int age
}
PERSON:::foo ||--|| CAR : owns
PERSON o{--|| HOUSE:::bar : has
classDef default fill:pink,stroke-width:4px
classDef foo stroke:#f90
classDef bar stroke:#ff0
classDef foobar stroke:#69f
การกำหนดค่า
เลย์เอาต์
เลย์เอาต์ของแผนภาพจัดการโดย render() เลย์เอาต์เริ่มต้นคือเลย์เอาต์ ELK ซึ่งเหมาะสำหรับแผนภาพขนาดใหญ่และซับซ้อนกว่า
หากต้องการเปลี่ยนไปใช้เลย์เอาต์ Dagre แบบคลาสสิก คุณสามารถตั้งค่าได้ใน config ของ YAML frontmatter
คัดลอก
ฝัง
---
config:
layout: dagre
---
เนื้อหาโค้ด Mermaid มีดังนี้:
คัดลอก
ฝัง
---
title: Order example
config:
layout: elk
---
erDiagram
CUSTOMER ||--o{ ORDER : places
ORDER ||--|{ LINE-ITEM : contains
CUSTOMER }|..|{ DELIVERY-ADDRESS : uses