العلاقات ومخطط الكيانات ERD
كيف ترسم العلاقات بين الأصناف، ثم تحوّلها إلى تصميم قاعدة بيانات بمفاتيح وجداول ربط؟ ⏱ نحو 45 دقيقة.
- ترسم علاقات الارتباط والوراثة والتركيب والتجميع مع التعدد.
- تميّز التركيب من التجميع والوراثة من الارتباط.
- تحوّل نموذج الأصناف إلى مخطط كيانات بمفاتيح أساسية وأجنبية.
- تحل علاقة كثير إلى كثير بجدول ربط.
١الأصناف لا تعيش وحدها
في الدرس السابق رسمنا الأصناف منفصلة. لكن المتطلبات مليئة بالعلاقات: «المدرب يقدّم دورات»، «الدورة تتكون من محاضرات»، «المتدرب نوع من المستخدمين». هذه العلاقات هي ما يحدد لاحقًا المفاتيح الأجنبية في قاعدة البيانات والروابط في الكود.
| العلاقة | الرمز في Mermaid | تعني | مثال |
|---|---|---|---|
| ارتباط | A "1" --> "0..*" B | A يعرف B، مع التعدد على الطرفين | مدرب واحد يقدّم صفرًا أو أكثر من الدورات |
| وراثة | User <|-- Trainee | Trainee نوع من User ويرث خصائصه | المتدرب والمدرب كلاهما مستخدم |
| تركيب | Course *-- Session | جزء لا يعيش دون الكل؛ يُحذف بحذفه | المحاضرات تُحذف إن حُذفت الدورة |
| تجميع | Course o-- Trainer | جزء مستقل يعيش وحده | المدرب يبقى إن حُذفت الدورة |
٢العلاقات في مخطط الأصناف
classDiagram class User["المستخدم"] { +int id +String name +String email } class Trainee["المتدرب"] class Trainer["المدرب"] { +String specialty } class Course["الدورة"] class Session["المحاضرة"] class Enrollment["التسجيل"] User <|-- Trainee User <|-- Trainer Course *-- "1..*" Session : تتكون من Trainer "1" --> "0..*" Course : يقدّم Trainee "1" --> "0..*" Enrollment : له Course "1" --> "0..*" Enrollment : فيها
اقرأ الأسهم: المثلث المفرغ <|-- وراثة، والمعيّن الممتلئ *-- تركيب، والأرقام على طرفي الارتباط التعدد. جرّب: أضف صنف Payment وارتباطًا «التسجيل له دفعة واحدة أو لا شيء»: Enrollment "1" --> "0..1" Payment.
| التعدد | يُقرأ | مثال |
|---|---|---|
1 | واحد بالضبط | لكل تسجيل متدرب واحد |
0..1 | صفر أو واحد | للتسجيل دفعة أو لا شيء (لم يدفع بعد) |
0..* أو * | صفر أو أكثر | للمتدرب صفر أو أكثر من التسجيلات |
1..* | واحد أو أكثر | للدورة محاضرة واحدة على الأقل |
٣من الأصناف إلى قاعدة البيانات: مخطط الكيانات
المواصفات التقنية تحتاج أيضًا تصميم قاعدة البيانات. مخطط الكيانات والعلاقات ERD يحوّل الأصناف التي تُحفظ إلى جداول، والعلاقات إلى مفاتيح:
erDiagram TRAINEE ||--o{ ENROLLMENT : "يسجّل" COURSE ||--o{ ENROLLMENT : "تضم" TRAINEE { int id PK string name string phone UK } COURSE { int id PK string title int capacity decimal fee } ENROLLMENT { int id PK int trainee_id FK int course_id FK string status }
PK المفتاح الأساسي، وFK المفتاح الأجنبي الذي يشير إلى جدول آخر، وUK قيمة لا تتكرر. العلاقة بين المتدرب والدورة كثير إلى كثير (متدرب في دورات كثيرة، ودورة لمتدربين كثيرين)، فتُحلّ بجدول ربط ENROLLMENT يحمل مفتاحين أجنبيين، وبياناته الخاصة مثل status.
| الرمز | المعنى |
|---|---|
|| | واحد بالضبط |
o| | صفر أو واحد |
o{ | صفر أو أكثر |
|{ | واحد أو أكثر |
يُقرأ TRAINEE ||--o{ ENROLLMENT: لكل تسجيل متدرب واحد بالضبط، ولكل متدرب صفر أو أكثر من التسجيلات. والمفتاح الأجنبي يوضع دائمًا في جدول طرف «الكثير».
٤قواعد التحويل
| في نموذج الأصناف | في قاعدة البيانات |
|---|---|
| صنف يُحفظ | جدول بمفتاح أساسي id |
| ارتباط واحد إلى كثير | مفتاح أجنبي في جدول طرف «الكثير» |
| ارتباط كثير إلى كثير | جدول ربط بمفتاحين أجنبيين (وقد يحمل بيانات إضافية) |
| واحد إلى واحد (اختياري) | مفتاح أجنبي فريد UK في أحد الطرفين |
عملية محسوبة مثل seatsLeft() | لا عمود لها؛ تُحسب بالاستعلام |
| وراثة | جدول واحد بعمود نوع type، أو جدول لكل صنف (قرار تصميمي يُوثَّق) |
توقّع وتحقّق
«المحاضرات تُحذف إن حُذفت الدورة» علاقة:
أين يوضع المفتاح الأجنبي في علاقة واحد إلى كثير؟
ماذا يعني الرمز o{ في ERD؟
كيف تُحل علاقة كثير إلى كثير بين المتدرب والدورة؟
الخلاصة
- ارتباط بتعدد، ووراثة «نوع من»، وتركيب يُحذف بحذف الكل، وتجميع مستقل.
- التعدد: 1، 0..1، 0..*، 1..* على طرفي العلاقة.
ERD: جداول بمفاتيحPKوFKوUK، ورموز || وo| وo{ و |{.- المفتاح الأجنبي في طرف «الكثير»، وكثير إلى كثير بجدول ربط.
تحدٍّ تصميمي: قاعدة بيانات «دورات»
متقدمأكمل مخطط الكيانات لنظام «دورات»: المدرب يقدّم دورات كثيرة، والمتدرب يسجّل في دورات كثيرة عبر جدول ENROLLMENT، ولكل تسجيل دفعة واحدة أو لا شيء.
- خمسة جداول: TRAINEE و COURSE و TRAINER و ENROLLMENT و PAYMENT، ولكل منها مفتاح أساسي PK.
- ENROLLMENT جدول ربط فيه trainee_id و course_id مفتاحين أجنبيين.
- COURSE فيه trainer_id مفتاحًا أجنبيًّا، وعلاقة واحد إلى كثير مع TRAINER.
- العلاقة بين ENROLLMENT و PAYMENT: واحد إلى صفر أو واحد.
- المخطط مكتوب بصيغة صحيحة ويُرسم
- نوعه: مخطط كيانات erDiagram
- جدول TRAINEE
- جدول COURSE
- جدول TRAINER
- جدول ENROLLMENT
- جدول PAYMENT
- COURSE له مفتاح أساسي
- PAYMENT له مفتاح أساسي
- ENROLLMENT فيه course_id FK
- COURSE فيه trainer_id FK
- متدرب واحد ← تسجيلات كثيرة
- دورة واحدة ← تسجيلات كثيرة
- مدرب واحد ← دورات كثيرة
- تسجيل واحد ← دفعة واحدة أو لا شيء
💡 تلميح 1
COURSE ||--o{ ENROLLMENT : "تضم".💡 تلميح 2
ENROLLMENT ||--o| PAYMENT : "تُدفع بـ".💡 تلميح 3
حاول بنفسك أولًا؛ للتصميم أكثر من حلّ صحيح، والفحص يتأكد من العناصر الأساسية فقط.
erDiagram TRAINEE ||--o{ ENROLLMENT : "يسجّل" COURSE ||--o{ ENROLLMENT : "تضم" TRAINER ||--o{ COURSE : "يقدّم" ENROLLMENT ||--o| PAYMENT : "تُدفع بـ" TRAINEE { int id PK string name } COURSE { int id PK int trainer_id FK string title } TRAINER { int id PK string name } ENROLLMENT { int id PK int trainee_id FK int course_id FK string status } PAYMENT { int id PK int enrollment_id FK decimal amount }