نمذجة البيانات ومخططات الكيانات والعلاقات

التسوية: الصيغة الأولى والثانية والثالثة

18 دقيقة الدرس 7 من 10

التسوية: الصيغة الأولى والثانية والثالثة

التكرار هو العدو الصامت لأي قاعدة بيانات مصمَّمة بعناية. حين تُخزَّن الحقيقة الواحدة في أكثر من مكان، تصبح عمليات التحديث محفوفةً بالمخاطر، وتؤدي عمليات الحذف إلى فقدان معلومات بالخطأ، فيما تفشل عمليات الإدراج حتى تتوفر بيانات غير ذات صلة. التسوية (Normalization) هي المنهجية المنضبطة للقضاء على ذلك التكرار خطوةً بخطوة، وذلك بإعادة هيكلة الجداول بحيث تعيش كل حقيقة في مكان واحد بالضبط.

ثمة صيغ تسوية عديدة، غير أن الغالبية العظمى من أنظمة الأعمال لا تحتاج سوى الصيغ الثلاث الأولى: 1NF و2NF و3NF. تُزيل هذه الصيغ معًا ثلاث فئات متمايزة من التكرار. سنستعرضها جميعًا عبر مثال متكامل مأخوذ من متجر كتب إلكتروني.

نقطة البداية: جدول غير مُسوَّى

تخيّل أن المتجر يحفظ بيانات الطلبات في جدول واحد بأسلوب جداول البيانات يُسمى OrderData:

OrderData --------------------------------------------------------------------------- OrderID | CustomerName | CustomerCity | BookISBN | BookTitle | AuthorNames | Qty | UnitPrice 1001 | Sara Khalid | Riyadh | 978-0-13-468599-1 | Clean Code | Robert Martin | 2 | 39.99 1001 | Sara Khalid | Riyadh | 978-0-13-235088-4 | The Clean Coder | Robert Martin | 1 | 34.99 1002 | Ali Hassan | Dubai | 978-0-13-468599-1 | Clean Code | Robert Martin | 3 | 39.99 1003 | Nour Salem | Cairo | 978-0-596-51774-8 | JavaScript | David Flanagan, O'Reilly| 1 | 49.99

لاحظ عدة مشكلات: يظهر Sara Khalid / Riyadh مرتين (تكرار)، وتحتوي خانة AuthorNames على قيمتين في خلية واحدة (انتهاك سنُصلحه أولًا)، فضلًا عن أن عنوان الكتاب لا يعتمد إلا على رقم ISBN وليس على الطلب الذي ورد فيه. هذه بالضبط هي الشذوذات التي تعالجها كل صيغة.

الصيغة الأولى (1NF): قيم ذرية ولا مجموعات متكررة

القاعدة: كل خلية يجب أن تحتوي على قيمة واحدة غير قابلة للتجزئة. لا توجد قوائم مفصولة بفاصلة، ولا مجموعات أعمدة متكررة (Author1، Author2 …). يجب أن يكون كل صف قابلًا للتعريف الفريد بمفتاح أساسي.

تحتوي خانة AuthorNames في الصف الرابع على قيمتين — وهذا انتهاك للصيغة الأولى. الحل: تقسيم البيانات متعددة القيم إلى صفوف منفصلة، أو نقلها إلى جدول مرتبط. نُحدد أيضًا المفتاح المركّب: (OrderID, BookISBN) يُعرِّف كل بند من بنود الطلب تعريفًا فريدًا.

OrderLine (بعد 1NF) --------------------------------------------------------------------------- OrderID | CustomerName | CustomerCity | BookISBN | BookTitle | AuthorID | AuthorName | Qty | UnitPrice 1001 | Sara Khalid | Riyadh | 978-0-13-468599-1 | Clean Code | A01 | Robert Martin | 2 | 39.99 1001 | Sara Khalid | Riyadh | 978-0-13-235088-4 | Clean Coder | A01 | Robert Martin | 1 | 34.99 1002 | Ali Hassan | Dubai | 978-0-13-468599-1 | Clean Code | A01 | Robert Martin | 3 | 39.99 1003 | Nour Salem | Cairo | 978-0-596-51774-8 | JavaScript | A02 | David Flanagan | 1 | 49.99 1003 | Nour Salem | Cairo | 978-0-596-51774-8 | JavaScript | A03 | O\'Reilly Press | 1 | 49.99
قائمة تحقق الصيغة الأولى: (1) لا خلايا متعددة القيم. (2) لا مجموعات أعمدة متكررة. (3) يوجد مفتاح أساسي. هذا كل شيء — الصيغة الأولى لا تهتم بالتكرار بين الأعمدة بعد.

الصيغة الثانية (2NF): لا اعتماديات جزئية

القاعدة: يجب أن يكون الجدول في الصيغة الأولى أولًا، ثم يجب أن يعتمد كل عمود غير مفتاحي على المفتاح الأساسي بأكمله — لا على جزء منه فحسب. تنطبق هذه القاعدة فقط حين يكون المفتاح الأساسي مركّبًا (مكوّنًا من أكثر من عمود).

في جدول الصيغة الأولى، المفتاح المركّب هو (OrderID, BookISBN, AuthorID). انظر إلى BookTitle: تعتمد فقط على BookISBN، لا على OrderID أو AuthorID. هذه اعتمادية جزئية. بالمثل، CustomerName وCustomerCity تعتمدان فقط على OrderID. ننقل كل مجموعة اعتمادية جزئية إلى جدول خاص بها:

Orders PK: OrderID OrderID | CustomerName | CustomerCity Books PK: BookISBN BookISBN | BookTitle | UnitPrice BookAuthors PK: (BookISBN, AuthorID) BookISBN | AuthorID | AuthorName OrderLines PK: (OrderID, BookISBN) OrderID | BookISBN | Qty

الآن لا يوجد أي عمود غير مفتاحي يخرج عن مفتاحه الكامل. اختفى تكرار Clean Code / 39.99 عبر الصفين 1001 و1002 — تلك المعلومة تعيش مرة واحدة في Books.

الصيغة الثالثة (3NF): لا اعتماديات انتقالية

القاعدة: يجب أن يكون الجدول في الصيغة الثانية أولًا، ثم يجب أن يعتمد كل عمود غير مفتاحي اعتمادًا مباشرًا على المفتاح الأساسي — لا على عمود غير مفتاحي آخر. حين يُحدد العمود B قيمة العمود C، لكن B نفسه عمود غير مفتاحي، فهذه اعتمادية انتقالية.

انظر إلى جدول Orders: لو أضفنا عمود CityRegion (مثلًا "Central" لـ Riyadh، و"Gulf" لـ Dubai)، فهذا العمود يعتمد على CustomerCity لا على OrderID مباشرةً. السلسلة هي: OrderID → CustomerCity → CityRegion. ننقله خارجًا:

Cities PK: CityName CityName | CityRegion Orders (بعد التعديل) PK: OrderID OrderID | CustomerName | CustomerCity ← CustomerCity مفتاح خارجي إلى Cities
خطأ شائع: يتوقف بعض المحللين عند الصيغة الثانية ظنًا أن المهمة اكتملت. الصيغة الثالثة بنفس الأهمية — تسبب الاعتماديات الانتقالية الشذوذات ذاتها التي تسببها الاعتماديات الجزئية. لو غيّرت منطقة مدينة في صف طلب واحد ونسيت الصفوف الأخرى، فستجد بيانات متناقضة.

رحلة التسوية الكاملة — مخطط

يعرض المخطط أدناه المراحل الثلاث على شكل جداول منفصلة، مع أسهم تُبيّن المفاتيح الخارجية التي تربطها. اعتبره ملخصًا لأين ينتهي كل مُعطى بعد التسوية.

Normalized tables after 1NF, 2NF, 3NF applied to the bookstore example Orders PK OrderID CustomerName FK CustomerCity OrderDate Cities PK CityName CityRegion Country OrderLines PK,FK OrderID PK,FK BookISBN Quantity LineTotal Books PK BookISBN BookTitle UnitPrice Genre BookAuthors PK,FK BookISBN PK AuthorID AuthorName AuthorRole Legend Foreign key reference PK = Primary Key FK = Foreign Key Italic attributes = optional/derived fields Each color = a different entity group
نموذج بيانات المتجر بعد التسوية الكاملة حتى الصيغة الثالثة: خمسة جداول، كل حقيقة مخزَّنة مرة واحدة.

اكتشاف كل انتهاك بسرعة

يستخدم المحللون قائمة تحقق ذهنية بسيطة عند مراجعة تصميم الجداول:

  • انتهاك الصيغة الأولى: هل تلاحظ قائمة مفصولة بفاصلة، أو مجموعة أعمدة مرقّمة (Phone1، Phone2 …)، أو خلية تحتوي بوضوح على أكثر من بيانة؟ أصلح ذلك أولًا.
  • انتهاك الصيغة الثانية: هل المفتاح الأساسي مركّب؟ إن كان كذلك، افحص كل عمود غير مفتاحي: هل يحتاج إلى جميع أجزاء المفتاح كي يكون ذا معنى، أم جزء منها فحسب؟ انقل الأعمدة ذات الاعتمادية الجزئية خارجًا.
  • انتهاك الصيغة الثالثة: هل يمكنك رسم سلسلة كـالمفتاح → العمود A → العمود B؟ إن كان العمود B يعتمد على العمود A (غير المفتاحي) لا على المفتاح مباشرةً، انقل العمود B خارجًا مع العمود A كمفتاح جديد له.
صيغة بويس-كود (BCNF) هي نسخة أكثر صرامة من الصيغة الثالثة تعالج حالات حافة نادرة تتعلق بمفاتيح مرشحة متداخلة. في الغالبية العظمى من أنظمة الأعمال، تكفي الصيغة الثالثة. ركّز عليها في مرحلة التحليل، وعُد إلى BCNF فقط إن ظهر هيكل مفاتيح غير معتاد.

شذوذات التحديث والإدراج والحذف

التسوية ليست تمرينًا أكاديميًا — بل تمنع ثلاث إخفاقات تشغيلية حقيقية:

  1. شذوذ التحديث: يظهر سعر كتاب في 50 صف من الطلبات. تغيير السعر يتطلب تعديله 50 مرة؛ إن فاتك صف واحد فالبيانات متناقضة. بعد التسوية، يعيش السعر مرة واحدة في Books.
  2. شذوذ الإدراج: في الجدول غير المُسوَّى لا يمكنك تسجيل كتاب جديد ما لم يُوجد طلب له (لأن OrderID جزء من المفتاح). بعد التسوية، تُدرج في Books باستقلالية تامة.
  3. شذوذ الحذف: لو حذفت الطلب الوحيد لكتاب معين، فقدت سجل الكتاب برمته. بعد التسوية، حذف طلب يمس OrderLines فحسب؛ يبقى Books سليمًا.
قاعدة إبهام: "كل عمود غير مفتاحي يجب أن يصف المفتاح، والمفتاح كاملًا، ولا شيء سوى المفتاح." يُلخص هذا المبدأ غير الرسمي بإتقان الصيغتين الثانية (المفتاح كاملًا) والثالثة (ولا شيء سوى المفتاح) معًا.

ملخص

تنقل التسوية الجدول عبر ثلاثة مستويات من الجودة. الصيغة الأولى تفرض القيم الذرية ومفتاحًا أساسيًا واضحًا. الصيغة الثانية تُزيل الاعتماديات الجزئية بحيث يحتاج كل عمود غير مفتاحي فعلًا إلى المفتاح المركّب بأكمله. الصيغة الثالثة تُزيل الاعتماديات الانتقالية بحيث تعتمد الأعمدة غير المفتاحية على المفتاح مباشرةً لا على بعضها. بعد هذه الخطوات الثلاث، تعيش كل بيانة تجارية في مكان واحد بالضبط — مما يجعل نموذج بياناتك متينًا ومتسقًا وسهل الصيانة.