انتقال للمقال
وقت القراءة: ≈ 20 دقيقة (بمعدل فنجان واحد من القهوة 😊)

استكمال مستويات الـ Database Normalization

السلام عليكم ورحمة الله وبركاته

المقدمة

في المقالة السابقة تطبيق الـ Database Normalization بشكل عملي تعرفنا على مفهوم الـ Database Normalization والصيغ الأساسية الثلاثة 1NF و 2NF و 3NF
وقلنا أن الوصول إلى الـ 3NF يكون كافيًا جدًا في معظم الحالات العملية لتصميم قاعدة بيانات منظمة وعملية

لكن هناك حالات خاصة ونادرة قد تحتاج فيها إلى صيغ متقدمة أخرى
في هذه المقالة سنشرح الصيغ المتقدمة وهى BCNF و 4NF و 5NF

صيغة بويس-كود Boyce-Codd Normal Form

الـ Boyce-Codd Normal Form أو اختصارًا نقول BCNF وهي صيغة متقدمة من الـ 3NF
وأحيانًا يطلق عليها الصيغة 3.5NF
والهدف من الـ BCNF هو التعامل مع بعض الحالات الخاصة التي قد لا تغطيها الـ 3NF بشكل كامل

بمعنى أننا في الـ 3NF نركز على التخلص من الـ Transitive Dependency
والتي تحدث عندما يكون لدينا عمود يعتمد على الـ PRIMARY KEY بشكل غير مباشر من خلال عمود آخر، بشرط أن العمود الأول لا يكون جزءًا من الـ PRIMARY KEY نفسه

قواعد الـ BCNF

الـ BCNF لا يشغل باله بالـ Transitive Dependency أو Partial Dependency بل يقدم قاعدة واحدة بسيطة

  1. يجب أن يكون الجدول في صيغة الـ 3NF
  2. كل Determinant يجب أن يكون Super Key

هذا هو التعريف التقني للـ BCNF، لكننا لا تعرف ما معنى Determinant و Super Key
لكن لا تقلق فسوف نفككها معا بشكل بسيط

ما هو الـ Determinant

لنبدأ بالسؤال الأول، ما معنى Determinant ؟
الـ Determinant هو ببساطة أي عمود أو مجموعة أعمدة يستطيع تحديد قيمة عمود آخر
لنفترض أننا لدينا جدول بسيط للطلاب يدعى Students ولدينا الأعمدة التالية idو name و email و city

نستطيع أن نقول أن العمود id يمكنه تحديد قيمة أي عمود آخر في الجدول
بمعنى أنه لو عرفنا قيمة الـ id، نستطيع أن نعرف اسم الطالب وبريده الإلكتروني والعمودينة التي يعيش فيها
لكن هل العمود name يمكنه تحديد قيمة عمود آخر ؟
الإجابة هي لا، لأن هناك احتمال وجود أكثر من طالب بنفس الاسم
بالتالي كلا العمودين name و city لا يمكنهما تحديد قيمة أي عمود آخر في الجدول

بالتالي نستطيع أن نقول أن العمود id هو Determinant
وأيضًا العمود email نستطيع أن نقول أنه Determinant لأن البريد الإلكتروني لا يتكرر في الجدول
أما العمودين name و city فهما ليسا Determinant

ما هو الـ Super Key

الآن ننتقل للسؤال الثاني، ما معنى Super Key ؟
الـ Super Key هو أي عمود أو مجموعة أعمدة لديها القدرة على تحديد الصف بالكامل
بمعنى أنه لو عرفنا قيمة الـ Super Key، نستطيع أن نعرف كل القيم في الصف
لاحظ أن الـ Determinant لا يشترط أن يكون العمود قادرًا على تحديد الصف بالكامل، بل يشترط فقط أنه يستطيع تحديد عمود واحد على الأقل
أما الـ Super Key فهو يشترط أن يكون العمود قادرًا على تحديد الصف بالكامل

في مثالنا السابق، العمود id هو Super Key لأنه يحدد الصف بالكامل
أي لو عرفنا قيمة الـ id نستطيع أن نعرف كل الأعمدة التي في الصف الذي يحتوي على هذا الـ id
كذلك العمود email هو أيضًا Super Key لأنه لا يتكرر في الجدول ويستطيع تحديد الصف بالكامل
أما العمودين name و city فهما ليسا Super Key لأنهما لا يستطيعان تحديد الصف بالكامل

الـ Super Key يمكن أن يكون أكثر من عمود
بحيث نقول أن {id, name} هو أيضًا Super Key لأنه يحتوي على الـ id الذي يحدد الصف بالكامل
و {email, city} هو أيضًا Super Key لأنه يحتوي على الـ email الذي يحدد الصف بالكامل
و {id, email, name, city} هو أيضًا Super Key لأنه يحتوي على الـ id والـ email اللذان يحددان الصف بالكامل

لكن {name, city} ليسا Super Key لأنه لا يستطيعان تحديد الصف بالكامل

ملحوظة: لاحظ أن كل Super Key لديه عمود واحد على الأقل Determinant
بالتالي نستطيع أن تقول أن كل Super Key هو أيضًا Determinant
لكن العكس غير صحيح، ليس كل Determinant هو Super Key

مثال عملي على الـ BCNF

الآن بعد أن فهمنا معنى Determinant و Super Key، نعود لتعريف الـ BCNF
الـ BCNF يقول لنا أن كل Determinant يجب أن يكون Super Key
بمعنى أننا لو وجدنا عمود ليس بـ Super Key ولكنه Determinant، فهذا يعني أن الجدول ليس في صيغة الـ BCNF

لنرى مثالًا عمليًا للتتضح الفكرة أكثر
لكن عليك أن تعرف أنه نادرًا ما نجد جداول لا تحقق الـ BCNF في الحياة العملية
فأنت حين تصل إلى الـ 3NF في معظم الحالات تكون قد وصلت إلى الـ BCNF أيضًا
في الغالبية العظمى من الحالات، الجداول التي تحقق الـ 3NF تحقق الـ BCNF أيضًا

على أي حال إليك هذا المثال البسيط فقط لتوضيح الفكرة
لدينا جدول يدعى Addresses يحتوي على الأعمدة التالية street, city, و zip

+-------------+-------+-------+
| street      | city  | zip   |
+-------------+-------+-------+
| Blog St     | Alex  | 10001 |
| Article St  | Alex  | 10002 |
| Ahmed St    | Cairo | 20001 |
| Tabarani St | Cairo | 20002 |
+-------------+-------+-------+

بافتراض أن الـ PRIMARY KEY في هذا الجدول هو مركب من العمودين street و city
ولدينا قاعدة تقول أن كل رمز بريدي zip ينتمي إلى مدينة واحدة فقط، وكل شارع في مدينة يحدد الرمز البريدي الخاص به

سؤال هل هذا الجدول يحقق الـ 1NF ؟ ستقول لي نعم
هل يحقق الـ 2NF ؟ ستقول لي نعم لأن zip يعتمد على الـ PRIMARY KEY بالكامل وليس على جزء منه، أي لا يوجد Partial Dependency هل يحقق الـ 3NF ؟ ستقول لي نعم لأنه لا يوجد أعمدة تعتمد على الـ PRIMARY KEY من خلال عمود آخر، أي لا يوجد Transitive Dependency

لكن هل يحقق صيغة الـ BCNF ؟ هنا الجواب سيكون لا

لمعرفة السبب دعنا نستخرج كل الـ Determinants و الـ Super Keys في هذا الجدول

  • الـ Determinants في هذا الجدول هي:
    • {street, city} لأنه الـ PRIMARY KEY ويحدد أي عمود في الصف
    • zip لأنه يستطيع تحديد الـ city بناءً على القاعدة التي تقول أن كل رمز بريدي ينتمي إلى مدينة واحدة فقط
  • الـ Super Keys في هذا الجدول هي:
    • {street, city} لأنه الـ PRIMARY KEY ويستطيع تحديد الصف بالكامل
    • {street, city, zip} لأنه يحتوي على الـ PRIMARY KEY الذي يحدد الصف بالكامل

لننظر الآن لتعريف الـ BCNF مرة أخرى والذي يقول أن كل Determinant يجب أن يكون Super Key
لاحظ أننا لدينا فقط اثنين Determinants وهما {street, city} و zip
والآن لنرى هل كلاهما Super Key ؟
سنجد أن {street, city} هو Super Key لأنه الـ PRIMARY KEY
أما zip فهو ليس Super Key لأنه لا يستطيع تحديد الصف بالكامل

بالتالي، لدينا Determinant ليس Super Key وهذا يعني أن الجدول لا يحقق صيغة الـ BCNF
لحل هذه المشكلة، علينا فصل الأعمدة التي تسبب هذه المشكلة في جداول منفصلة

هنا، يمكننا إنشاء جدول منفصل للرموز البريدية والمدن ولنسميه اسم عام وليكن PostalCodes

+----+-------+-------+
| id | zip   | city  |
+----+-------+-------+
| 1  | 10001 | Alex  |
| 2  | 10002 | Alex  |
| 3  | 20001 | Cairo |
| 4  | 20002 | Cairo |
+----+-------+-------+

والآن في الجدول الأصلي Addresses سنقوم بحذف عمود city و zip
ونستبدل عمود zip بـ postal_code_id الذي سيكون الـ FOREIGN KEY الذي يربط بين جدول الـ Addresses وجدول الـ PostalCodes

+-------------+----------------+
| street      | postal_code_id |
+-------------+----------------+
| Blog St     | 1              |
| Article St  | 2              |
| Ahmed St    | 3              |
| Tabarani St | 4              |
+-------------+----------------+

الآن كلا الجدولين في BCNF

الصيغة الرابعة Fourth Normal Form

الـ Fourth Normal Form أو اختصارًا 4NF هي صيغة متقدمة أكثر من الـ BCNF
وهى تتعامل وتركز على حل مشكلة تدعى Multi-valued Dependency

قواعد الـ 4NF

يمكننا تلخيص قواعد الـ 4NF في قاعدتين واضحتين:

  1. يجب أن يكون الجدول في صيغة الـ BCNF
  2. لكل Non-Trivial Multi-valued Dependency من نوع {X} ->-> {Y}، يجب أن يكون {X} هو Super Key للجدول

قد تبدو القاعدة الثانية غامضة قليلًا الآن، لكن لا تقلق، سنشرح معنى كل مصطلح خطوة بخطوة كما نفعل دائمًا
ونحن بالفعل قد شرحنا معنى الـ Super Key في الـ BCNF، لذا سنركز الآن على شرح معنى الـ Multi-valued Dependency و Non-Trivial Multi-valued Dependency

الـ Multi-valued Dependency

يحدث الـ Multi-valued Dependency عندما يكون لدينا عمود كل قيمة فيه تربط بأكثر من قيمة في عمود آخر
ويكون كلا العمودين مستقلين عن بعضهما البعض بحيث أن القيم في كلا العمودين لا تعتمد على بعضهما البعض ولا يربطهما شيء أي لا توجد رابط منطقي بينهما

هل تتذكر مصلح الـ Determinant الذي شرحناه في الـ BCNF ؟
حين قلنا أن الـ Determinant هو أي عمود أو مجموعة أعمدة يستطيع تحديد قيمة عمود آخر في نفس الصف
الآن في حالة الـ Multi-valued Dependency، لدينا عمود يستطيع تحديد عدة قيم في أكثر من عمود في أكثر من صف

بمعنى أننا لو افترضنا أن هناك جدول يدعى Students يحتوي ثلاث أعمدة student_name و course و club
وكان لدينا البيانات التالية في الجدول:

+----------------+-----------------+------------+
| student_name   | course          | club       |
+----------------+-----------------+------------+
| Ahmed Moustafa | Database        | Football   |
| Ahmed Moustafa | Database        | Basketball |
| Ahmed Moustafa | Database        | Chess      |
| Ahmed Moustafa | Web Development | Football   |
| Ahmed Moustafa | Web Development | Basketball |
| Ahmed Moustafa | Web Development | Chess      |
| Osama Ali      | Database        | Football   |
| Osama Ali      | Database        | Chess      |
| Osama Ali      | Data Structures | Football   |
| Osama Ali      | Data Structures | Chess      |
+----------------+-----------------+------------+

وقلنا أن الـ PRIMARY KEY في هذا الجدول هو مركب من الأعمدة الثلاثة {student_name, course, club}
وهو أيضًا الـ Super Key الوحيد في هذا الجدول

لاحظ أن قيمة Ahmed ترتبط بعدة قيم في العمود course وهي Database و Web Developement
وأيضًا قيمة Ahmed ترتبط بعدة قيم في العمود club وهي Football و Basketball و Chess نفس الشيء ينطبق على Osama

ونستطيع أن نعبر عن هذا الترابط بهذا الشكل:

{student_name} ->-> {course}
{student_name} ->-> {club}

هكذا نمثل أن العمود student_name لديه Multi-valued Dependency على العمود course
وأن العمود student_name لديه Multi-valued Dependency على العمود club

لكن لاحظ أن قيم العمودين course و club لا يعتمدان على بعضهما البعض ولا يربطهم شيء ببعضهما البعض
فمثلًا، لا يوجد ترابط حقيقي أو منطقي بين الـ Database أو Web Developement وبين الـ Football أو Basketball
في هذه الحالة نقول أن هناك استقلالية بين العمودين course و club
وأن كلاهما يعتمدان فقط على العمود student_name بشكل مباشر

هذا هو التعريف الحقيقي للـ Multi-valued Dependency
حيث أن العمود A يستطيع تحديد أكثر من قيمة في العمود B و أكثر من قيمة في العمود C
وكلا العمودين B و C مستقلين عن بعضهما البعض
في هذه الحالة فقط، نستطيع أن نقول أن هناك Multi-valued Dependency بين العمود A وكل من العمودين B و C

تذكر أننا نرمز للـ Multi-valued Dependency بهذا الشكل {A} ->-> {B}
حيث أن {A} يمثل عمود أو مجموعة أعمدة لها Multi-valued Dependency على عمود أو مجموعة أعمدة متمثلة في {B}

الـ Non-Trivial Multi-valued Dependency

المصطلح الآخر الذي يجب أن نفهمه هو الـ Non-Trivial Multi-valued Dependency
أولًا لنفهم ما معنى Trivial Multi-valued Dependency لكي نفهم ما هو الـ Non-Trivial Multi-valued Dependency
نحن نقول على الـ Multi-valued Dependency أنه Trivial عندما يكون الترابط {A} ->-> {B} يحقق أحد الشرطين التاليين:

  • أن يكون {B} جزءًا من العمود {A} في حالة كان {A} يرمز إلى مجموعة أعمدة
  • أن مجموع {B} و {A} يعطينا كل الأعمدة في الجدول

إذا لم يتحقق أي من الشرطين السابقين، نقول أن الـ Multi-valued Dependency هو Non-Trivial
في مثال جدول الـ Cars كان لدينا Multi-valued Dependency بهذا الشكل:

{student_name} ->-> {course}
{student_name} ->-> {club}

ستلاحظ أن كلاهما لا يحقق أي من الشرطين السابقين، لأن {course} ليست جزءًا من {student_name}
وكذلك {club} ليست جزءًا من {student_name}
وأيضًا مجموع {student_name} و {course} لا يعطينا كل الأعمدة في الجدول
وكذلك مجموع {student_name} و {club} لا يعطينا كل الأعمدة في الجدول
بالتالي كلاهما Non-Trivial Multi-valued Dependency

وطالما أن كلاهما أن {student_name} ->-> {course} و {student_name} ->-> {club} هما Non-Trivial Multi-valued Dependency
وأن العمود student_name ليس Super Key في الجدول بل هو جزء منه
تذكر حين قلنا أننا نملك PRIMARY KEY المركب يتمثل في الأعمدة {student_name, course, club} وأيضًا هو الـ Super Key الوحيد في الجدول

بالتالي نستطيع أن نقول أن الجدول لا يحقق الـ 4NF


وهذا يجعلنا عرضة لمشاكل الـ Data Anomalies التي تحدث بسبب الـ Multi-valued Dependency فعلى سبيل المثال، لو أردنا إضافة دورة جديدة Data Science لطالب معين Ahmed
ستجد أنك تضطر إلى تكرار نفس الطالب Ahmed ونفس الـ course لكل club لنفس الطالب
وبما أن الطالب Ahmed ينتمي إلى Football و Basketball و Chess
حينها ستضطر إلى إضافة ثلاث صفوف في الجدول، واحد لكل club وهذا يعد Insertion Anomaly لأنك قد تنسى إضافة صف معين لـ club معين
أو ماذا إذا أردنا التخلص من كل الـ clubs لكن مع الحفاظ على الـ courses الخاصة بالطالب
حينها ماذا نفعل ؟
إذا قمنا بحذف كل الـ clubs الخاصة بالطالب، سنفقد أيضًا كل الـ courses الخاصة بهذا الطالب وهذا يعد Deletion Anomaly
ونفس الشيء ينطبق على التعديل، فإذا أردنا تعديل club خاص بطالب معين، يجب علينا تعديله في كل الصفوف الخاصة بهذا الطالب مع كل course وهذا يعد Update Anomaly بحيث أننا قد ننسى تعديل صف معين

مثال عملي على الـ 4NF

الآن بعد أن فهمنا معنى الـ Multi-valued Dependency و Non-Trivial Multi-valued Dependency، نعود لتعريف الـ 4NF
والذي كان يقول لنا أن لكل Non-Trivial Multi-valued Dependency من نوع {X} ->-> {Y}، يجب أن يكون {X} هو Super Key للجدول بالتالي نفهم أنه لو وجدنا رابطة من نوع {X} ->-> {Y} وكانت {X} ليست Super Key في الجدول
حينها نقول أن الجدول لا يحقق الـ 4NF

أو يمكنك أن تنظر لها من الزاوية الأخرى، حيث أنه في حالة وجدنا رابطة من نوع {X} ->-> {Y} وكانت {Y} لا تعتمد على Super Key
حينها نقول أن الجدول لا يحقق الـ 4NF

لنرى مثالًا أخر لكي نشرح كيف نحل هذه المشكلة ونحقق الـ 4NF
لنتخيل أننا لدينا جدول يدعى Products يحتوي على الأعمدة التالية product_name و supplier_name و category

+--------------+-----------------+-----------------+
| product_name | supplier_name   | category        |
+--------------+-----------------+-----------------+
| Laptop       | Ibrahim Basha   | Electronics     |
| Laptop       | Ibrahim Basha   | Computers       |
| Laptop       | Ashraf Habibi   | Electronics     |
| Laptop       | Ashraf Habibi   | Computers       |
| Phone        | BlueEyes Man    | Electronics     |
| Phone        | BlueEyes Man    | Mobile Devices  |
| Phone        | Kamal Akhoya    | Electronics     |
| Phone        | Kamal Akhoya    | Mobile Devices  |
+--------------+-----------------+-----------------+

في هذا الجدول، لنفترض أن الـ PRIMARY KEY هو مركب الأعمدة الثلاثة product_name و supplier_name و category
بمجرد النظر إلى الجدول، ستلاحظ الـ Multi-valued Dependency:

{product_name} ->-> {supplier_name}
{product_name} ->-> {category}

هنا لدينا product_name يستطيع تحديد أكثر من قيمة في كلا العمودين supplier_name و category
ولا يوجد ترابط منطقي بين msupplier_name و category أي كلاهما مستقلين عن بعضهما البعض m لذالك نستطيع أن نقول أن هناك Multi-valued Dependency بين product_name وكل من supplier_name و category
والآن لنرى هل هذه الـ Multi-valued Dependency هي Trivial أم Non-Trivial

لدينا هنا Super Key واحد فقط في الجدول وهو {product_name, supplier_name, category}
وبمجرد النظر ستعرف أنها Non-Trivial Multi-valued Dependency

لأن كلاهما لا يحقق أي من الشرطين
بحيث أن الشرط الأول ينص على أنه يجب على العمودين supplier_name أو category أن يعتمدا على Super Key وهو {product_name, supplier_name, category}
لكنهما يعتمدان فقط على product_name وهو ليس Super Key في الجدول
والشرط الثاني ينص على أنه يجب أن يكون مجموع {product_name} مع {supplier_name} أو {category} يعطينا كل الأعمدة في الجدول
لكن مجموع {product_name} مع {supplier_name} يعطينا فقط {product_name, supplier_name}
وكذلك مجموع {product_name} مع {category} يعطينا فقط {product_name, category}
وهذا لا يعطينا كل الأعمدة في الجدول

لذا نستطيع أن نقول أن الجدول لا يحقق الـ 4NF

بالطبع هذا أيضًا يجعلنا عرضة لمشاكل الـ Data Anomalies التي تحدث بسبب الـ Multi-valued Dependency
كما ذكرنا سواء في المثال السابق فلو أردت إضافة أو تعديل أو حذف بيانات خاصة بـ supplier_name أو category ستجد نفسك مضطرًا إلى التعامل مع كلاهما في نفس الوقت
برغم أنهما مستقلين عن بعضهما البعض
عندما تريد أن تتعامل مع الـ supplier_name، تضطر إلى التفكير في الـ category والعكس صحيح وهذا ينتج منك أن تقوم بعمل query لتعرف ما هي الـ categories الخاصة بمنتج معين ثم تقوم بعمل query آخر لتعرف ما هي الـ suppliers الخاصة بنفس المنتج ثم تبدأ في التفكير في كيفية التعامل معهما معًا
هذه هى مشكلة الـ Non-Trivial Multi-valued Dependency التى نريد حلها

لحل هذه المشكلة والوصول للـ 4NF، علينا فصل كل Multi-valued Dependency في جدول مستقل
بحيث أن الـ supplier_name و category لا يعتمدان على بعضهما البعض
لكنهما يعتمدان فقط على product_name
بالتالي نستطيع ربط product_name مع الـ supplier_name في جدول منفصل وليكن ProductSuppliers
ونربط الـ product_name مع الـ category في جدول آخر منفصل وليكن ProductCategories

بالتالي جدول الـ ProductSuppliers سيكون كالتالي:

+--------------+---------------+
| product_name | supplier_name |
+--------------+---------------+
| Laptop       | Ibrahim Basha |
| Laptop       | Ashraf Habibi |
| Phone        | Kamal Akhoya  |
| Phone        | BlueEyes Man  |
+--------------+---------------+

مكون فقط من الأعمدة product_name و supplier_name

وجدول ProductCategories سيكون كالتالي:

+--------------+----------------+
| product_name | category       |
+--------------+----------------+
| Laptop       | Electronics    |
| Laptop       | Computers      |
| Phone        | Electronics    |
| Phone        | Mobile Devices |
+--------------+----------------+

مكون فقط من الأعمدة product_name و category

هكذا فصلنا الـ supplier_name و category في جدولين مستقلين
بالتالي لو أردنا اضافة supplier جديد لمنتج معين، نضيفه في جدول ProductSuppliers فقط
دون أن نشغل بالنا بأي category خاصة بهذا المنتج
ولو أردت معرفة الـ category الخاصة بهذا المنتج، ننظر في جدول ProductCategories فقط
الآن بعد هذا التقسيم، كل جدول يمثل الرابطة بينه وبين الـ product_name فقط لا غير دون أن يؤثر على الآخر
ولا نتعب أنفسنا في التفكير بالـ supplier_name عندما نتعامل مع الـ category والعكس صحيح
حتى إذا أردت حذف كل الـ suppliers أو الـ categories الخاصة بمنتج معين، لن يؤثر ذلك على الآخر

الآن كل الجداول أصبحت تحقق الـ 4NF


الـ 4NF صيغة متقدمة لا نحتاجها في معظم الحالات العملية
لكنها مفيدة جدًا عندما نواجه جداول تحتوي على Non-Trivial Multi-valued Dependencies
بحيث لديك أعمدة مستقلة عن بعضها البعض وتعتمد فقط على عمود مشترك
وعندما تركز على عمود معين، تجد نفسك مضطرًا إلى التفكير في الأعمدة الأخرى برغم أنه لا يوجد بينها أي ترابط منطقي
مثل ما رأينا في مثال جدول الـ Products مع الـ supplier_name و category

أيضًا يمكننا عمل جدول يدعى Products يحتوي على id و product_name فقط
ثم نجعل product_id هو الـ FOREIGN KEY في كلا الجدولين ProductSuppliers و ProductCategories
لكي لا نكرر اسم المنتج في كلا الجدولين ونستخدم الـ id فقط

ونفس الأمر ينطبق على supplier_name و category يمكننا عمل جداول منفصلة لكل منهما Suppliers و Categories
بحيث تحتوي على id و name فقط
ثم نجعل supplier_id هو الـ FOREIGN KEY في جدول ProductSuppliers
ونجعل category_id هو الـ FOREIGN KEY في جدول ProductCategories

الصيغة الخامسة Fifth Normal Form

الـ Fifth Normal Form أو اختصارًا 5NF وتسمى أيضًا Project-Join Normal Form أو PJNF
وهي أعلى مستوى من الـ Normalization وتتعامل مع مشكلة تدعى Join Dependency

قواعد الـ 5NF

يمكننا تلخيص قواعد الـ 5NF كالتالي:

  1. يجب أن يكون الجدول في صيغة الـ 4NF
  2. لا يمكن تقسيم الجدول إلى جداول أصغر بدون فقدان البيانات

بمعنى آخر، إذا كان بإمكاننا تقسيم الجدول إلى جداول أصغر ثم إعادة تجميعها باستخدام عملية الـ JOIN دون فقدان أي بيانات
فهذا يعني أن الجدول لا يحقق الـ 5NF ويجب تقسيمه

الـ Join Dependency

الـ Join Dependency هو مفهوم يتعلق بالقدرة على تقسيم جدول إلى جداول أصغر ثم إعادة تجميعها
بحيث أن البيانات الناتجة بعد التقسيم واعادة جمعها تكون مطابقة تمامًا للبيانات الأصلية قبل التقسيم

نقول أن الجدول لديه Join Dependency إذا كنا نستطيع بناءه وتمثيله عن طريق عملية JOIN لجداول أصغر منه
وهذا يعني أن الجدول يحتوي على بيانات مكررة ضمنيًا أو غير ضرورية في معظم الحالات
ويمكن استنتاج هذه البيانات من الجداول الأصغر عند طبيق عملية الـ JOIN

وما أقصه بجملة بيانات غير ضرورية في معظم الحالات هو أن وجود هذه البيانات في الجدول قد لا يستخدم في أغلب الأوقات
ويمكننا احضارها فقط عند الحاجة من الجداول الأصغر عن طريق عملية الـ JOIN لذا بدلًا من أن تظل في الجدول الكبير دون الحاجة إليها في معظم الحالات، من الأفضل تقسيم الجدول إلى جداول أصغر بالتالي نقلل من حجم البيانات المخزنة في الجدول الكبير ويقلل حجم الـ query ويسهل علينا إدارة البيانات في الجداول الأصغر

والأمر يشبه إلى حد كبير ما فعلناه في الـ 4NF عندما فصلنا الـ Multi-valued Dependency في جداول أصغر
لأننا طالما أننا نستطيع تقسيم الجدول إلى جداول أصغر ثم إعادة تجميعها باستخدام عملية الـ JOIN دون فقدان أي بيانات
فهذا يعني أننا نتعامل Join Dependency

ما الفرق بين الـ 4NF و الـ 5NF؟

لكن ما الفرق اذًا بين الـ 4NF و الـ 5NF ؟
لأنك عندما تنظر إلى أمثلة الـ 4NF و الـ 5NF ستجد أنها متشابهة جدًا
وقد يتوهم لبعض أنها نفس الشيء لأن النتيجة النهائية في الحالتين هي تقسيم الجدول الكبير إلى جداول أصغر
لكن الفرق هو في سبب التقسيم ونوع الترابط بين الأعمدة التي نقوم بتقسيمها

ببساطة شديدة، الفرق هو:

  • في الـ 4NF: نحن نفصل أعمدة لا رابطة لها ببعضها البعض نهائيًا، لكنها اجتمعت في جدول واحد بالخطأ
  • في الـ 5NF: نحن نفصل أعمدة مرتبطة ببعضها البعض، لكن يمكننا استنتاج الرابطة الكبيرة من روابط أصغر

ففي الـ 4NF، تخيل أننا لدينا الأعمدة A و B و C
وكان العمود A مرتبط بمجموعة من البيانات في العمود B ومجموعة أخرى من البيانات في العمود C
لكن العمودين B و C لا يوجد بينهما أي رابط منطقي فمثلا لدينا الطالب A الذي يدرس في مواد B وينضم إلى النوادي C
هنا لا يوجد أي رابط منطقي بين المواد التي يدرسها الطالب و النوادي التي ينضم إليها
ثم ووجدنا أنهما Non-Trivial Multi-valued Dependency لا يعتمدان على Super Key في الجدول
لذا قلنا أنه يجب علينا فصلهم في جداول مستقلة

أما في الـ 5NF، تخيل أننا لدينا الأعمدة A و B و C
وكان العمود A مرتبط بمجموعة من البيانات في العمود B ومجموعة أخرى من البيانات في العمود C
لكن هذه المرة، العمودين B و C مرتبطين ببعضهما البعض
أو يوجد إعتماد دائري بينهم بحيث أن A مرتبط بـ B و B مرتبط بـ C و C مرتبط بـ A
أو أننا لو امتلكنا قيمة عمودين A و B نستطيع أن نستنتج قيمة العمود C وهكذا

ثم وجدنا أننا يمكننا فصل الجدول إلى جدولين أصغر واحد يحتوي على الأعمدة A و B والآخر يحتوي على الأعمدة A و C
وجدول ثالث يحتوي على الأعمدة B و C إذا احتجنا ذلك في حالة الاعتماد الدائري الذي ذكرناها
ووجدنا أنه يمكننا إعادة تجميعهم في جدول واحد باستخدام عملية الـ JOIN دون فقدان أي بيانات
لذا قلنا أنه يجب علينا فصلهم في جداول مستقلة

ستلاحظ أن الأسباب مختلفة بين الـ 4NF و الـ 5NF لكن النتيجة النهائية هي نفسها

مثال عملي على الـ 5NF

لنفترض أن لدينا جدول يدعى Authors يحتوي على الأعمدة التالية author_name, book, و publisher

+---------------+------------------+-------------------+
| author_name   | book             | publisher         |
+---------------+------------------+-------------------+
| Ahmed Mostafa | Learning SQL     | Tabarani Tech Ltd |
| Ahmed Mostafa | Learning SQL     | Code Masters Inc  |
| Ahmed Mostafa | Web Development  | Tabarani Tech Ltd |
| Ahmed Mostafa | Web Development  | Code Masters Inc  |
| Osama Ali     | Data Science     | Data World Pub    |
| Osama Ali     | Data Science     | Tech Books Co     |
| Osama Ali     | Machine Learning | Data World Pub    |
| Osama Ali     | Machine Learning | Tech Books Co     |
+---------------+------------------+-------------------+

الـ PRIMARY KEY في هذا الجدول هو مركب من الأعمدة الثلاثة {supplier_name, product_name, project}

سؤال هل هذا الجدول يحقق الـ 4NF ؟
ستلاحظ أن هناك Multi-valued Dependency بين author_name و book وبين author_name و publisher
بحيث أن author_name يستطيع تحديد أكثر من قيمة في كلا العمودين book و publisher
هل هذه الـ Multi-valued Dependency هي Non-Trivial ؟
نعم هي Non-Trivial لأن كلاهما لا يحقق أي من الشرطين الذين ذكرناهم سابقًا في شرح الـ 4NF
لكن هل العمودين book و publisher مستقلين عن بعضهما البعض ؟
الجواب هو لا، لأن هناك رابطة منطقية بينهما بحيث أن الكتاب الذي يكتبه المؤلف يتم نشره بواسطة ناشر معين
بالتالي الكتاب يحتاج إلى ناشر لكي يتم نشره
والناشر يحتاج إلى كتاب لكي ينشره

هنا يوجد رابطة منطقية بين العمودين book و publisher
لذا الجدول يحقق الـ 4NF لأنه طالماأن الأعمدة مرتبطة ببعضها البعض وليست مستقلة عن بعضها البعض
ففي هذه الحالة لا يمكننا أن نقول أن هناك Multi-valued Dependency بين أعمدة مستقلة عن بعضها البعض

الآن، هل هذا الجدول يحقق الـ 5NF ؟

لاحظ أنه برغم من أنه يحقق الـ 4NF، لكنه يملك جميع المشاكل الخاصة بالـ Data Anomalies التي ذكرناها في مثال الـ 4NF
بحيث أننا لو أردنا إضافة كتاب جديد لمؤلف معين، يجب علينا إضافة صف جديد لكل ناشر ينشر هذا الكتاب
ولو أردنا حذف كل الناشرين لكتاب معين، سنفقد أيضًا اسم الكتاب والمؤلف
ولو أردنا تعديل ناشر معين لكتاب معين، يجب علينا تعديله في كل الصفوف الخاصة بهذا الكتاب والمؤلف

وهذا يشير إلى أننا نستطيع تقسيم هذا الجدول إلى جداول أصغر
لكن بناءًا على شرط الـ 5NF، بحيث يجب أن نتأكد من أننا يمكننا إعادة تجميعهم في جدول واحد باستخدام عملية الـ JOIN دون فقدان أي بيانات

بالنظر إلى الأعمدة الثلاثة author_name و book و publisher
ستجد رابطة ثلاثية بينهم بحيث أن:

  • author_name مرتبط بـ book بحيث أن المؤلف يكتب الكتاب
  • author_name مرتبط بـ publisher بحيث أن المؤلف يتعاقد مع الناشر
  • book مرتبط بـ publisher بحيث أن الكتاب يتم نشره بواسطة الناشر

هنا نعرف أن هناك Join Dependency بين الأعمدة الثلاثة
وبالتالي نستطيع تقسيم الجدول إلى جداول أصغر تمثل كل رابطة ثنائية بينهم
وبالتالي نستطيع إعادة تجميعهم في جدول واحد باستخدام عملية الـ JOIN دون فقدان أي بيانات

وهذه الجداول ستكون كالتالي جدول AuthorsBooks وجدول AuthorsPublishers وجدول BooksPublishers

جدول AuthorsBooks سيتكون فقط من الأعمدة author_name و book

+---------------+------------------+
| author_name   | book             |
+---------------+------------------+
| Ahmed Mostafa | Learning SQL     |
| Ahmed Mostafa | Web Development  |
| Osama Ali     | Data Science     |
| Osama Ali     | Machine Learning |
+---------------+------------------+

وجدول AuthorsPublishers سيتكون فقط من الأعمدة author_name و publisher

+---------------+-------------------+
| author_name   | publisher         |
+---------------+-------------------+
| Ahmed Mostafa | Tabarani Tech Ltd |
| Ahmed Mostafa | Code Masters Inc  |
| Osama Ali     | Data World Pub    |
| Osama Ali     | Tech Books Co     |
+---------------+-------------------+

وجدول BooksPublishers سيتكون فقط من الأعمدة book و publisher

+------------------+-------------------+
| book             | publisher         |
+------------------+-------------------+
| Learning SQL     | Tabarani Tech Ltd |
| Learning SQL     | Code Masters Inc  |
| Web Development  | Tabarani Tech Ltd |
| Web Development  | Code Masters Inc  |
| Data Science     | Data World Pub    |
| Data Science     | Tech Books Co     |
| Machine Learning | Data World Pub    |
| Machine Learning | Tech Books Co     |
+------------------+-------------------+

الآن الجداول الثلاثة تحقق الـ 5NF

لاحظ الآن إذا قرر Ahmed Mostafa كتابة كتاب جديد Advanced SQL
سنقوم بإضافة صف جديد في جدول AuthorsBooks و نضيف صف جديد في جدول BooksPublishers لكل ناشر ينشر هذا الكتاب
برغم من أننا نحتاج إلى إضافة صفوف في جداول متعددة، لكننا نتجنب مشاكل الـ Data Anomalies التي كانت تحدث في الجدول الكبير
بحيث أننا لو أردنا حذف كل الناشرين فستظل الكتب والمؤلفين محفوظين في جدول AuthorsBooks
ولو أردنا حذف كل الكتب فستظل المؤلفين والناشرين محفوظين في جدول AuthorsPublishers
وهكذا نتجنب كل مشاكل الـ Insertion Anomaly و Deletion Anomaly و Update Anomaly

على أي حال كيف نتحقق من صحة التقسيم ؟

للتحقق من أن التقسيم صحيح، يمكننا إجراء عملية JOIN على الجداول الثلاثة
والنتيجة يجب أن تكون مطابقة تمامًا للجدول الأصلي

SELECT AuthorsBooks.author_name, AuthorsBooks.book, BooksPublishers.publisher
FROM AuthorsBooks
JOIN AuthorsPublishers ON AuthorsBooks.author_name = AuthorsPublishers.author_name
JOIN BooksPublishers ON AuthorsBooks.book = BooksPublishers.book;

النتيجة ستكون:

+---------------+------------------+-------------------+
| author_name   | book             | publisher         |
+---------------+------------------+-------------------+
| Ahmed Mostafa | Learning SQL     | Tabarani Tech Ltd |
| Ahmed Mostafa | Learning SQL     | Code Masters Inc  |
| Ahmed Mostafa | Web Development  | Tabarani Tech Ltd |
| Ahmed Mostafa | Web Development  | Code Masters Inc  |
| Osama Ali     | Data Science     | Data World Pub    |
| Osama Ali     | Data Science     | Tech Books Co     |
| Osama Ali     | Machine Learning | Data World Pub    |
| Osama Ali     | Machine Learning | Tech Books Co     |
+---------------+------------------+-------------------+

وهذه هي نفس البيانات الموجودة في الجدول الأصلي لذا التقسيم صحيح ويحقق الـ 5NF


وكما قلنا في الـ 5NF، يمكننا عمل جدول يدعى Authors يحتوي على id و author_name فقط
وجدول Books يحتوي على id و book فقط
وجدول Publishers يحتوي على id و publisher فقط
ثم نتعامل مع الجداول الثلاثة AuthorsBooks و AuthorsPublishers و BooksPublishers باستخدام الـ FOREIGN KEY بدلاً من أسماء المؤلفين والكتب والناشرين مباشرةً


لاحظ أن النواتج التي توصلنا إليها في الـ 5NF و الـ 4NF تسمى Pivot Tables
وهى جداول وسيطة تربط بين جدولين وتكون رابطة Many-to-Many بين الجدولين
وغالبًا في الـ Pivot Tables ما يتم تخزين فقط الـ id الخاص بكل جدول فقط لا غير

ملخص مستويات الـ Normalization

لنلخص ما تعلمناه في هذه المقالة و المقالة السابقة تطبيق الـ Database Normalization بشكل عملي:

الصيغة الهدف الأساسي المشكلة التي تحلها
1NF التخلص من التكرار في القيم والأعمدة قيم متعددة في خانة واحدة أو أعمدة مكررة
2NF التخلص من الـ Partial Dependency أعمدة تعتمد على جزء من الـ PRIMARY KEY
3NF التخلص من الـ Transitive Dependency أعمدة تعتمد على الـ PRIMARY KEY بشكل غير مباشر
BCNF كل Determinant يكون Super Key حالات خاصة لا تغطيها الـ 3NF
4NF التخلص من الـ Multi-valued Dependency أعمدة مستقلة بذاتها ترتبط بعمود واحد
5NF التخلص من الـ Join Dependency روابط بين الأعمدة يمكن تقسيمها في جداول أصغر واعادة تجميها بسهولة

في معظم الحالات العملية، الوصول إلى الـ 3NF يكون كافيًا جدًا لتصميم قاعدة بيانات منظمة وعملية
لكن معرفة الصيغ المتقدمة مثل BCNF و 4NF و 5NF تساعدك في فهم المشاكل التي قد تواجهها في الحالات النادرة

أتمنى أن تكون هذه المقالة قد ساعدتك في فهم الصيغ المتقدمة لتطبيع قواعد البيانات
وأن تكون الآن قادرًا على تطبيق هذه القواعد في تصميم قواعد البيانات الخاصة بك