الدرس 9: تحسين النموذج (Normalization)
تخيل أنك تُصمم مطبخًا. قد تضع كل الأدوات في درج واحد كبير (قاعدة بيانات غير مسواة)، وهذا قد يعمل في البداية. لكن بمرور الوقت، ستجد صعوبة في العثور على ما تريده، وقد تكرر شراء نفس الأداة لأنك لا تجدها، وقد تفقد أدوات مهمة. التسوية هي عملية تنظيم المطبخ بوضع الملاعق مع الملاعق، والشوك مع الشوك، والسكاكين في مكانها المخصص. هذا يجعل المطبخ أكثر كفاءة، وأسهل في الاستخدام والصيانة.
1. مقدمة حول التسوية
أ. تعريف التسوية وأهدافها الأساسية
التسوية (Normalization) هي عملية منهجية لتنظيم الجداول في قاعدة بيانات علائقية لتقليل تكرار البيانات (Data Redundancy) وتحسين التكامل المرجعي (Referential Integrity). بعبارة أخرى، هي فن تقسيم الجداول الكبيرة إلى جداول أصغر وأكثر تنظيمًا، مع الحفاظ على العلاقات بينها.
الأهداف الأساسية للتسوية:
- تقليل تكرار البيانات: منع تخزين نفس المعلومة في أماكن متعددة.
- تحسين التكامل المرجعي (Data Integrity): التأكد من أن البيانات متسقة وصحيحة.
- القضاء على الشذوذ (Anomalies): التخلص من المشاكل عند إدراج أو تحديث أو حذف البيانات.
- جعل تصميم قاعدة البيانات أكثر مرونة: تسهيل إضافة ميزات جديدة أو تعديل الهيكل.
ب. المشاكل التي تحلها التسوية في قواعد البيانات
التسوية تعالج مجموعة من المشاكل المعروفة باسم "الشذوذ" (Anomalies):
- التكرار (Redundancy): تخزين نفس المعلومة في عدة أماكن.
- شذوذ الإدراج (Insertion Anomaly): عدم القدرة على إدخال معلومات عن كيان ما دون وجود معلومات عن كيان آخر.
- شذوذ التحديث (Update Anomaly): الحاجة إلى تحديث نفس البيانات في عدة أماكن مختلفة.
- شذوذ الحذف (Deletion Anomaly): فقدان معلومات مهمة عن كيان ما عند حذف سجل مرتبط به.
ج. العلاقة بين التسوية وتصميم قواعد البيانات الفعالة
التسوية ليست مجرد خطوة تقنية؛ إنها فلسفة لتصميم قاعدة بيانات قوية وفعالة. قاعدة البيانات المسواة بشكل جيد تكون أكثر كفاءة، وموثوقية، وأسهل في الصيانة والتطوير.
2. مشاكل قواعد البيانات غير المسواة (أمثلة عملية)
لنأخذ مثالاً لجدول "غير مسوّى" لنرى هذه المشاكل بوضوح:
رقم_الطالب | اسم_الطالب | تاريخ_الميلاد | رقم_المقرر | اسم_المقرر | ساعات_المقرر | اسم_الاستاذ | قسم_الاستاذ |
---|---|---|---|---|---|---|---|
101 | أحمد سعيد | 2003-05-15 | CS101 | برمجة 1 | 3 | محمد علي | علوم الحاسب |
101 | أحمد سعيد | 2003-05-15 | MA202 | رياضيات متقدمة | 4 | سارة خالد | الرياضيات |
102 | ليلى محمد | 2004-11-22 | CS101 | برمجة 1 | 3 | محمد علي | علوم الحاسب |
103 | كريم علي | 2003-01-30 | EC303 | اقتصاد كلي | 3 | ريم فهد | الاقتصاد |
3. الأشكال العادية (Normal Forms)
الأشكال العادية (Normal Forms - NFs) هي مجموعة من القواعد أو المستويات التي تساعدنا على تنظيم الجداول وتقليل الشذوذ. الهدف عادةً هو الوصول إلى 3NF.
أ. الشكل العادي الأول (1NF - First Normal Form)
القواعد: القيم الذرية (Atomic Values)، عدم تكرار المجموعات، كل عمود له اسم فريد، كل صف له مفتاح أساسي فريد.
ب. الشكل العادي الثاني (2NF - Second Normal Form)
القاعدة: يجب أن يكون الجدول في 1NF، ويجب ألا يكون هناك تبعية جزئية (Partial Dependency) لأي عمود غير مفتاحي على جزء من المفتاح الأساسي المركب.
ج. الشكل العادي الثالث (3NF - Third Normal Form)
القاعدة: يجب أن يكون الجدول في 2NF، ويجب ألا يكون هناك تبعية متعدية (Transitive Dependency) لأي عمود غير مفتاحي على المفتاح الأساسي.
د. أشكال عادية متقدمة (اختياري)
توجد أشكال متقدمة مثل BCNF, 4NF, 5NF لمعالجة حالات خاصة ومعقدة تتجاوز متطلبات معظم التصميمات العملية.
4. المفاهيم الأساسية المساعدة
- التبعية الوظيفية (Functional Dependency - FD): قيمة عمود (A) تحدد بشكل فريد قيمة عمود آخر (B). نرمز لها: A → B.
- المفاتيح الأساسية (Primary Keys): العمود الذي يحدد بشكل فريد كل صف.
- المفاتيح المرشحة (Candidate Keys): أي عمود يمكن أن يعمل كمفتاح أساسي.
- المفاتيح الخارجية (Foreign Keys): عمود يشير إلى المفتاح الأساسي في جدول آخر.
- التبعية المتعدية (Transitive Dependency): A → B و B → C (حيث B ليس جزءًا من المفتاح الأساسي).
- التبعية الجزئية (Partial Dependency): عمود غير مفتاحي يعتمد على جزء فقط من المفتاح الأساسي المركب.
5. عملية التسوية خطوة بخطوة
- ابدأ بجدول واحد (أو مجموعة جداول أولية).
- حدد جميع المفاتيح والتبعيات الوظيفية.
- طبّق قواعد 1NF: تأكد من أن كل عمود يحتوي على قيم ذرية.
- طبّق قواعد 2NF: ابحث عن التبعيات الجزئية وقم بتقسيم الجدول.
- طبّق قواعد 3NF: ابحث عن التبعيات المتعدية وقم بتقسيم الجدول.
- التحقق من سلامة البيانات بعد التسوية.
6. أمثلة تطبيقية شاملة
هنا ملخص لكيفية تسوية المثالين المذكورين في النص الأصلي للوصول إلى 3NF.
مثال 1: نظام إدارة الطلاب والمقررات
النتيجة النهائية (في 3NF):
- الطلاب: (StudentID, StudentName, StudentDOB)
- المقررات: (CourseID, CourseName, CourseHours, ProfessorID (FK))
- تسجيلات_المقررات: (StudentID (FK), CourseID (FK))
- الاساتذة: (ProfessorID, ProfessorName, DeptName (FK))
- الاقسام: (DeptName)
مثال 2: نظام المبيعات والعملاء
النتيجة النهائية (في 3NF):
- العملاء: (CustomerID, CustomerName, CustomerAddress)
- المنتجات: (ProductID, ProductName, ProductPrice)
- الطلبات: (OrderID, OrderDate, CustomerID (FK))
- تفاصيل_الطلب: (OrderID (FK), ProductID (FK), Quantity)
7. أمثلة تطبيقية شاملة (مع الحل التفصيلي)
سنقوم الآن بتطبيق عملية التسوية خطوة بخطوة على المثالين المذكورين في النص، لنرى كيف ننتقل من جدول غير مسوّى إلى مجموعة جداول في الشكل العادي الثالث (3NF).
مثال 1: نظام إدارة الطلاب والمقررات
أ. الحالة الأولية (جدول غير مسوّى)
الجدول الأصلي: Student_Course_Info
المفتاح الأساسي الافتراضي: مركب من (StudentID
, CourseID
).
StudentID | StudentName | StudentDOB | CourseID | CourseName | CourseHours | ProfessorName | ProfessorDept |
---|---|---|---|---|---|---|---|
101 | أحمد سعيد | 2003-05-15 | CS101 | برمجة 1 | 3 | محمد علي | علوم الحاسب |
ب. تحديد التبعيات الوظيفية (FDs)
StudentID → StudentName, StudentDOB
(معلومات الطالب تعتمد على رقم الطالب)CourseID → CourseName, CourseHours, ProfessorName
(معلومات المقرر تعتمد على رقم المقرر)ProfessorName → ProfessorDept
(قسم الأستاذ يعتمد على اسم الأستاذ)
ج. التحويل إلى 1NF
الجدول بالفعل في 1NF لأن كل حقل يحتوي على قيمة ذرية (فردية) ولا توجد مجموعات متكررة.
د. التحويل إلى 2NF (حل التبعية الجزئية)
نلاحظ أن هناك تبعيات جزئية لأن بعض الأعمدة لا تعتمد على المفتاح الأساسي المركب (StudentID
, CourseID
) بأكمله:
StudentName
وStudentDOB
يعتمدان فقط علىStudentID
.CourseName
,CourseHours
,ProfessorName
يعتمدون فقط علىCourseID
.
الحل: نقوم بتقسيم الجدول الأصلي إلى ثلاثة جداول جديدة للتخلص من هذه التبعيات الجزئية.
الجداول الناتجة في 2NF:
- جدول الطلاب:
Students(StudentID, StudentName, StudentDOB)
- جدول المقررات:
Courses(CourseID, CourseName, CourseHours, ProfessorName, ProfessorDept)
- جدول تسجيل المقررات (ربط):
Course_Enrollments(StudentID, CourseID)
هـ. التحويل إلى 3NF (حل التبعية المتعدية)
نركز الآن على الجداول الناتجة. نجد أن جدول Courses
لا يزال يعاني من مشكلة: تبعية متعدية.
CourseID → ProfessorName → ProfessorDept
هنا، ProfessorDept
(عمود غير مفتاحي) يعتمد على ProfessorName
(عمود غير مفتاحي آخر)، وليس مباشرة على المفتاح الأساسي CourseID
.
الحل: نقوم بتقسيم جدول Courses
مرة أخرى، ومن الأفضل استخدام ProfessorID
كمعرف فريد للأستاذ.
و. النتيجة النهائية (في 3NF)
بعد إتمام عملية التسوية، نحصل على خمسة جداول منظمة:
- الطلاب:
Students(StudentID, StudentName, StudentDOB)
- الأقسام:
Departments(DeptName)
- الأساتذة:
Professors(ProfessorID, ProfessorName, DeptName (FK))
- المقررات:
Courses(CourseID, CourseName, CourseHours, ProfessorID (FK))
- تسجيلات_المقررات:
Course_Enrollments(StudentID (FK), CourseID (FK))
مثال 2: نظام المبيعات والعملاء
أ. الحالة الأولية (جدول غير مسوّى)
الجدول الأصلي: Sales_Order
المفتاح الأساسي الافتراضي: مركب من (OrderID
, ProductID
).
ب. تحديد التبعيات الوظيفية (FDs)
OrderID → OrderDate, CustomerID, CustomerName, CustomerAddress
CustomerID → CustomerName, CustomerAddress
ProductID → ProductName, ProductPrice
(OrderID, ProductID) → Quantity
(الكمية تعتمد على الطلب والمنتج معاً)
ج. التحويل إلى 2NF (حل التبعية الجزئية)
لدينا تبعيات جزئية واضحة:
- معلومات الطلب والعميل تعتمد فقط على
OrderID
. - معلومات المنتج تعتمد فقط على
ProductID
.
الحل: نقسم الجدول إلى ثلاثة جداول.
الجداول الناتجة في 2NF:
- جدول الطلبات:
Orders(OrderID, OrderDate, CustomerID, CustomerName, CustomerAddress)
- جدول المنتجات:
Products(ProductID, ProductName, ProductPrice)
- جدول تفاصيل الطلب:
Order_Details(OrderID, ProductID, Quantity)
د. التحويل إلى 3NF (حل التبعية المتعدية)
نركز على جدول Orders
. نجد فيه تبعية متعدية:
OrderID → CustomerID → (CustomerName, CustomerAddress)
CustomerName
و CustomerAddress
يعتمدان على CustomerID
(عمود غير مفتاحي)، وليس مباشرة على OrderID
.
الحل: نقسم جدول Orders
.
هـ. النتيجة النهائية (في 3NF)
بعد إتمام التسوية، نحصل على أربعة جداول نظيفة ومنظمة:
- العملاء:
Customers(CustomerID, CustomerName, CustomerAddress)
- المنتجات:
Products(ProductID, ProductName, ProductPrice)
- الطلبات:
Orders(OrderID, OrderDate, CustomerID (FK))
- تفاصيل_الطلب:
Order_Details(OrderID (FK), ProductID (FK), Quantity)