مخطط ER
يصف مخطط الكيانات والعلاقات (ويُختصر ER diagram) الأشياء المترابطة التي تهمّ مجالًا معرفيًا معيّنًا. ويتكوّن مخطط 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 لتنفيذ علاقة واحد إلى متعدد باستخدام مصفوفة دون خصائص مفتاح أجنبي. وبالمثل، يمكن للغات البرمجة كائنية التوجه استخدام المؤشرات أو المراجع إلى المجموعات. وحتى بالنسبة للنماذج الموجّهة إلى التنفيذ العلائقي، قد يُرى أن تضمين خصائص المفتاح الأجنبي سيكرّر عرض المعلومات التي تعرضها العلاقة، ولن يضيف معنى إلى الكيان.
الصيغة
الكيانات والعلاقات
تتوافق صيغة 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". يرجى ملاحظة النقطتين في وسط العلاقة، وهذا سيؤدي إلى رسم خط متقطّع بين الكيانين. ولكن عندما تُحلّ هذه العلاقة متعدد إلى متعدد إلى علاقتين من واحد إلى متعدد، نلاحظ أنه بدون PERSON و CAR لا يمكن وجود NAMED-DRIVER. وتصبح هذه العلاقات تعريفية، وتُمثَّل بشرطة، أي خط متصل:
| القيمة | الاسم البديل |
|---|---|
| -- | تعريفي |
| .. | غير تعريفي |
| القيمة | 别名为 |
|---|---|
| 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;
ملاحظة: للتنسيقات المخصّصة أو عبارات الفئات الأخرى الأولوية، وهي تتجاوز التنسيق الافتراضي. (على سبيل المثال، تضع فئة 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