عبدالفتاح الصلوي

أخطاء شائعة عند التعامل مع Primary Key و Foreign Key وكيفية تجنبها

كتبها : عبدالفتاح الصلوي / في رصيف : قواعد البيانات

أخطاء شائعة عند التعامل مع Primary Key  و Foreign Key  وكيفية تجنبها

المفاتيح الخارجية هي عنصر مهم في أي قاعدة بيانات علائقية. ولكن قد يكون من السهل نسبيًا حدوث مشكلات إذا لم يتم إعداد المفاتيح الخارجية (Foreign keys) بشكل صحيح.

دعونا نلقي نظرة اولاً على بعض المفاهيم والقيود، قبل ان نتطرق إلى أكثر الاخطاء شيوعاً عند التعامل مع  المفاتيح الاجنبية (الخارجية). حيث سنتعرف على مفهوم قيود المفتاح الأساسي والمفتاح الخارجي. 


قيود المفتاح الأساسي Primary Key Constraints

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

قيود المفتاح الخارجي (الاجنبي) Foreign Key Constraints

المفتاح الخارجي (FK) هو عمود أو مجموعة من الأعمدة تُستخدم لإنشاء ارتباط بين البيانات في جدولين و للتحكم في البيانات التي يمكن تخزينها في جدول المفتاح الخارجي.

عند استخدام مفتاح أساسي في جدول آخر، يصبح هذا العمود مفتاحًا خارجيًا في الجدول الثاني. على سبيل المثال لو كان لدينا قاعدة بيانات للمبيعات ولدينا جدول طلبات البيع (sales_order) وهذا الجدول يحتوي على ارتباط مفتاح أجنبي مع جدول مندوبي المبيعات (sales_preson) نظراً لوجود علاقة منطقية بين مندوبي المبيعات وطلبات البيع. فمثلاً في جدول مندوبي المبيعات لنفرض أننا انشأنا مفتاح أساسي باسم (id)  فإن هذا العمود  سيصبح مفتاح أجنبي FK في جدول طلبات البيع . وبإنشاء قيد المفتاح الخارجي هنا (sales_person_id) لا يمكن إدراج قيمة في حقل معرف مندوب المبيعات في جدول أوامر البيع (sales_order)ما لم تكن هذه القيمة موجودة مسبقاً في جدول مندوبي المبيعات. أي اننا لا يمكن أن نختار مندوب المبيعات قبل أن يتم إضافته في جدول مندوبي المبيعات (sales_person).

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

1. خطأ عدم تطابق حجم ونوع البيانات Mismatched data types

يحدث هذا الخطأ عند محاولة إنشاء أو استخدام مفتاح خارجي للإشارة إلى عمود في جدول آخر (مفتاح أساسي)، حيث يجب أن تكون أنواع البيانات في كلا الجدولين متطابقة ولانها ليست كذلك فسيحدث هذا الخطا (mismatched data types). على سبيل المثال ، إذا كان العمود المرجعي حيث تقوم بتعريف المفتاح الخارجي يستخدم نوع بيانات INT (رقم صحيح)  ، فيجب أن يكون العمود المشار إليه من نوع INT أيضاً بنفس الحجم (نفس السعة التخزينية) .

في بعض أنظمة قواعد البيانات سيظهر خطأ عند محاولة إنشاء قيد الارتباط ومع ذلك ، اعتمادًا على نظام قاعدة البيانات الذي تستخدمه ومخطط قاعدة البيانات المحدد ، قد يجد البعض صعوبة في معرفة سبب خطأ عدم تطابق نوع بيانات المفتاح الخارجي. وفي بعض أنظمة قواعد البيانات يتم انشاء الجدول والقيد بنجاح، ولكن في المستقبل قد تحدث أخطاء وحينها قد تبدو غير منطقية أو مفهومة بالنسبة للمطور والمستخدم على حد سواء. فعلى سبيل المثال باستخدام قاعدة بيانات SQLite ، من الممكن إنشاء جدول بنجاح مع عدم تطابق نوع بيانات المفتاح الخارجي.
حجم البيانات أو سعة التخزين في حقل المفتاح الأساسي و حقل المفتاح الأجنبي يجب أن تكون متساوية. وكمثال إذا كنت مبرمج تستخدم إطار عمل laravel  مع mysql  وأردت تحديث مشروع ما، فربما تكون قد واجهت مشكلة عند محاولة انشاء علاقة في مشروع يستخدم نسخة قديمة من إطار العمل. حيث كان يتم تعريف حقل  ال id في الجداول بشكل افتراضي على أنه integer  (4-byte) ومن ثم تم تعديله في الإصدارات اللاحقة إلى Biginteger  (8-byte) فعادة سيظهر لك خطأ لعدم تطابق حجم البيانات .

أسهل طريقة لتجنب هذه المشكلة هي التأكد من أن جميع الأعمدة المرتبطة ببعضها البعض بقيود مفاتيح خارجية تشترك في نفس نوع البيانات عند إنشاء الجداول في المقام الأول.
إذا كنت تقوم بتعديل جدول موجود بقيد مفتاح خارجي جديد ، فمن المحتمل أن يعرض نظام قاعدة البيانات خطأً إذا حاولت إنشاء قيد مفتاح خارجي يربط الأعمدة بأنواع بيانات مختلفة.
 وكخلاصة اذا ظهر لك هذا الخطأ فعليك التأكد أولاً من أن نوع البيانات وحجمها متساوي في عمودي المفتاح الأساسي والمفتاح الأجنبي. 

2. وجود المفاتيح الخارجية المتدلية  dangling foreign key

هل لفت نظرك مصطلح التدلي؟ لا بد أنه مبهم! لا تقلق.

يقصد بالمفاتيح المتدلية أن القيمة الموجودة في حقل المفتاح الأجنبي ليست موجودة في حقل  المفتاح الرئيسي. فالمفتاح الخارجي المتدلي (dangling foreign key) هو مفتاح خارجي يرتبط بجدول أو عمود غير موجود. ، وسبب حدوث ذلك - في الغالب يتم إنشاء مفاتيح خارجية متدلية عن طريق الخطأ. أو بسبب عدم فرض التكامل المرجعي بين الجدولين وحينها عند حذف الصف في الجدول الأساسي لا يتم تحديث البيانات في الجدول او الجداول المرتبطة.
في بعض أنظمة إدارة قواعد البيانات ، يمكنك إنشاء مفتاح خارجي متدلي ببساطة عن طريق إنشاء جدول مرجعي أولاً ، وتضمين قيد المفتاح الخارجي الذي يربطه بعمود في الجدول التالي الذي تخطط لإنشائه. إذا نسيت بعد ذلك إنشاء الجدول أو العمود المشار إليه ، فلديك مفتاح خارجي متدلي.

بشكل أكثر شيوعًا ، تظهر المفاتيح الخارجية المتدلية عند إزالة جدول أو عمود تمت الإشارة إليه في مكان آخر في قاعدة البيانات. تسمح بعض أنظمة إدارة قواعد البيانات بذلك ، مما يترك مفتاحًا خارجيًا متدليًا ، بينما يتسبب البعض الآخر في حدوث أخطاء. في بعض انظمة قواعد البيانات ، ستؤدي محاولة حذف جدول مشار إليه بواسطة مفتاح خارجي لجدول آخر في ظهور خطأ أو تنبيه .إذا كنت تحاول حذف عمود أو جدول ولكنك تتلقى خطأ بشكل متكرر ، فقد يكون ذلك بسبب محاولتك إزالة شيء يمثل قيد مفتاح خارجي في جدول آخر.
أفضل طريقة لتجنب تدلي المفاتيح الخارجية هي ببساطة استخدام نظام قاعدة بيانات حديث يمكنه التحقق من صحة القيود عند إضافتها إلى جدول، ولن يسمح ذلك للمستخدمين بفك تكامل مرجعية قاعدة البيانات عن طريق إزالة البيانات المطلوبة بواسطة جدول آخر . وبالطبع تتذكر عبارة (CASCADE) و nullOnDelete ومثيلاتهن من خصائص القيود عند انشاء مفتاح أجنبي ، نعم فتلك العبارات تهدف إلى التكامل المرجعي وحل مثل هذه المشكلة.
 أفضل وسيلة دفاع تالية ضد مشكلة تدلي المفاتيح الخارجية هي التوثيق الجيد لقاعدة البيانات والذي يبرز علاقات المفاتيح الخارجية والتخطيط الدقيق ، خاصة إذا تمت إزالة أي شيء من قاعدة البيانات.


3. عدم إنشاء فهارس مفاتيح خارجية عند إضافة مفاتيح خارجية foreign key indexes

يختلف هذا "الخطأ" قليلاً عن الخطأين السابقين. حيث أن عدم إنشاء فهرس لمفتاح خارجي لن ينتج عنه رسالة خطأ. ومع ذلك ، فإن الفشل في استخدام فهارس المفاتيح الخارجية قد يعني أنك تتعرض عن غير قصد لضربة كبيرة في الأداء ، لا سيما عندما تعمل تعمل على قاعدة بيانات كبيرة.
يمكن لفهارس المفاتيح الخارجية تحسين أداء الاستعلامات التي تتضمن الصلات بين الجداول بشكل ملحوظ. يختلف عدد المرات التي يؤدي فيها تطبيقك هذه الصلات ، ولكن في معظم الحالات يكون إنشاء فهرس مفتاح خارجي مضمونًا لأنه سيسمح لتلك الاستعلامات بتجنب إجراء فحص كافة البيانات في الجدول. (عمليات البحث والفلترة في كل بيانات الجدول بطيئة ومكلفة ومن الأفضل تجنبها.)
الشيء المهم الذي يجب تذكره هنا هو أنه بينما يتم إنشاء الفهارس الأساسية تلقائيًا عند تعيين مفتاح أساسي للجدول ، يجب عادةً إنشاء فهارس المفاتيح الخارجية بشكل يدوي في بعض انظمة قواعد البيانات. 
لا تتخطى هذه الخطوة! حتى إذا كنت تعمل على قاعدة بيانات صغيرة بما يكفي بحيث لا تُحدث تحسينات الأداء فرقًا ذا مغزى حتى الآن ، فمن الأفضل وضع الفهارس في مكانها الصحيح. بخلاف ذلك ، أثناء التوسع والنمو ، يمكن أن يتحول اختلاف بضع أجزاء من الألف من الثانية في قاعدة بيانات الاختبار الصغيرة إلى تأخر يشعر به المستخدمون في قاعدة البيانات  عند نشر  التطبيق للاستخدام الفعلي.
في mysql   innodb  يتم  تلقائياً انشاء index  بينما في sql server  لا تتم فهرسة التلقائية على حد علمي. وعليه فيجب التأكد من نظام قاعدة البيانات الذي تعمل عليه.

4- استخدام روابط غير رسمية بدلاً من المفاتيح الخارجية

 يعد عدم استخدام قيود المفاتيح الخارجية ، أكبر خطأ في قواعد البيانات العلائقية. حيث يتم بدلاً من ذلك الاعتماد على روابط غير رسمية بين جداول قواعد البيانات. وكمثال توضيحي:

تخيل أن قاعدة بيانات المبيعات لدينا فيها جدول للعملاء المتوقعين (الأفراد) وجدول منفصل للشركات.

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

خاتمة

في نهاية المطاف، فإن معظم أخطاء المفاتيح الخارجية الشائعة هي نتيجة التخطيط غير المتقن لقاعدة البيانات. يمكن أن يساعدك قضاء بعض الوقت الإضافي عند إنشاء مخطط قاعدة البيانات على تجنب الصداع لاحقًا.
تتضمن الأسئلة الجيدة التي يجب أن تطرحها على نفسك في مرحلة التخطيط ما يلي:
• ما هي الجداول التي سيحتاج هذا الجدول / العمود إلى الارتباط بها؟
• ماذا سيحدث إذا تمت إزالة هذا الجدول / العمود؟
• ما هو حجم البيانات وكيف يمكن أن يتغير في المستقبل ؟
أكرر من المفيد دائمًا قضاء وقت إضافي في التفكير في الهندسة المعمارية وكيف سيتم توسيع نطاق عملك قبل البدء في بناء قاعدة البيانات.

علامات ذات صلة :