يعرف أي شخص قام بتطوير تطبيق ويب صغير يعتمد على قاعدة بيانات باستخدام MySQL أن إنشاء الجداول واسترجاعها وتحديثها وحذفها في قاعدة بيانات علائقية هي عمليات بسيطة نسبيًا. من الناحية النظرية، طالما أنك تتقن استخدام عبارات SQL الأكثر شيوعًا وعلى دراية بلغة البرمجة النصية من جانب الخادم التي اخترت استخدامها، فهذا يكفي للتعامل مع العمليات المختلفة المطلوبة في جداول MySQL، خاصة عند استخدام MyISAM السريع محرك قاعدة البيانات متى. ولكن حتى في أبسط الحالات، فإن الأمور أكثر تعقيدًا مما نعتقد. أدناه نستخدم مثالا نموذجيا للتوضيح. لنفترض أنك تدير موقع مدونة تقوم بتحديثه كل يوم تقريبًا، ويتيح الموقع للزائرين التعليق على منشوراتك.
في هذه الحالة، يجب أن يتضمن مخطط قاعدة البيانات لدينا جدولين MyISAM على الأقل، أحدهما لتخزين منشورات مدونتك والآخر للتعامل مع تعليقات الزوار. من الواضح أن هناك علاقة رأس بأطراف بين هذين الجدولين، لذلك نحتاج إلى تحديد مفتاح خارجي في الجدول الثاني حتى يمكن الحفاظ على سلامة قاعدة البيانات عند تحديث صفوف البيانات أو حذفها.
بالنسبة لتطبيق مثل ما سبق، لا يمثل الحفاظ على سلامة الجدولين تحديًا خطيرًا فحسب، بل تكمن الصعوبة الأكبر في أنه يتعين علينا الحفاظ على سلامتهما على مستوى التطبيق. هذا هو النهج المتبع أثناء التطوير لمعظم مشاريع الويب التي لا تتطلب استخدام المعاملات لأن جداول MyISAM توفر أداءً ممتازًا.
بالطبع، يأتي هذا أيضًا بتكلفة كما قلت سابقًا، يجب أن يحافظ التطبيق على سلامة قاعدة البيانات واتساقها، مما يعني تنفيذ منطق برمجة أكثر تعقيدًا للتعامل مع العلاقات بين الجداول المختلفة. على الرغم من أنه يمكن تبسيط الوصول إلى قاعدة البيانات من خلال استخدام طبقات التجريد ووحدات ORM، مع زيادة عدد جداول البيانات التي يتطلبها التطبيق، فإن المنطق المطلوب للتعامل معها سيصبح بلا شك أكثر تعقيدًا.
لذا، بالنسبة لـ MySQL، هل هناك أي طريقة لمعالجة المفتاح الخارجي على مستوى قاعدة البيانات للمساعدة في الحفاظ على سلامة قاعدة البيانات؟ تتيح لنا هذه الميزة تشغيل إجراءات معينة، مثل تحديث وحذف صفوف بيانات معينة في الجدول للحفاظ على العلاقات المحددة مسبقًا.
كل شيء له إيجابيات وسلبيات، والعيب الرئيسي لاستخدام جداول InnoDB هو أنها أبطأ من MyISAM، خاصة في التطبيقات واسعة النطاق حيث يجب الاستعلام عن العديد من الجداول. لحسن الحظ، يدعم جدول MyISAM في الإصدار الأحدث من MySQL أيضًا قيود المفاتيح الخارجية.
ستقدم هذه المقالة كيفية تطبيق قيود المفاتيح الخارجية على جداول InnoDB. بالإضافة إلى ذلك، سوف نستخدم فئة MySQL مجردة بسيطة تعتمد على PHP لإنشاء نموذج التعليمات البرمجية ذي الصلة، وبالطبع يمكنك أيضًا استخدام اللغة الأخرى المفضلة لديك من جانب الخادم. الآن، نبدأ في تقديم كيفية تطبيق قيود المفاتيح الخارجية على MySQL.
متى يتم استخدام قيود المفتاح الخارجي
لنكون صادقين، عند استخدام جداول InnoDB في MySQL، ليس من الضروري استخدام قيود المفاتيح الخارجية، ومع ذلك، لغرض قيود المفاتيح الخارجية في مواقف معينة، سنستخدم كود المثال المذكور سابقًا للشرح بالتفصيل. يتضمن جدولين MyISAM، يستخدمان لتخزين منشورات وتعليقات المدونة.
عند تحديد مخطط قاعدة البيانات، نحتاج إلى إنشاء علاقة رأس بأطراف بين الجدولين عن طريق إنشاء مفتاح خارجي في الجدول حيث يتم تخزين التعليقات لتعيين صفوف البيانات (أي التعليقات) لمقالة محددة. فيما يلي رمز SQL الأساسي لإنشاء نموذج جدول MyISAM:
قم بإسقاط الجدول إذا كان موجودًا `test`.`blogs`;
إنشاء جدول `اختبار`.`مدونات` (
`id` INT(10) AUTO_INCREMENT غير الموقعة،
نص "العنوان" ،
"المحتوى" النص،
"المؤلف" VARCHAR(45) افتراضي فارغ،
مفتاح بريروز (`المعرف`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
قم بإسقاط الجدول إذا كان موجودًا `test`.`comments`;
إنشاء جدول `اختبار`.`تعليقات` (
`id` INT(10) AUTO_INCREMENT غير الموقعة،
`blog_id` INT(10) غير موقعة افتراضيًا فارغة،
نص "التعليق"،
"المؤلف" VARCHAR(45) افتراضي فارغ،
مفتاح بريروز (`المعرف`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
أعلاه، قمنا للتو بتعريف جدولي MyISAM، اللذين يشكلان طبقة البيانات لتطبيق المدونة. كما ترون، الجدول الأول يسمى المدونات، ويتكون من بعض الحقول الواضحة، والتي تُستخدم لتخزين المعرف والعنوان والمحتوى لكل منشور في المدونة، وأخيرًا المؤلف. الجدول الثاني يسمى التعليقات، والذي يُستخدم لتخزين التعليقات المتعلقة بكل منشور مدونة، ويستخدم معرف منشور المدونة كمفتاح خارجي لإنشاء علاقة رأس بأطراف.
حتى الآن، كانت مهمتنا سهلة لأننا أنشأنا جدولين بسيطين فقط في MyISAM. بعد ذلك، ما نريد القيام به هو ملء هذه الجداول ببعض السجلات لتوضيح ما يجب فعله في الجدول الآخر عند حذف إدخال في الجدول الأول.
تحديث والحفاظ على سلامة قاعدة البيانات
في الجزء السابق، قمنا بإنشاء جدولي MyISAM ليكونا بمثابة طبقة بيانات لتطبيق المدونة. وبطبيعة الحال، لا تزال المقدمة المذكورة أعلاه بسيطة للغاية، ونحن بحاجة إلى مناقشتها بشكل أكبر. للقيام بذلك، سنقوم بملء هذه الجداول ببعض السجلات باستخدام أوامر SQL كما يلي:
إدراج في المدونات (المعرف، العنوان، المحتوى، المؤلف) القيم (NULL، "عنوان إدخال المدونة الأول"، "محتوى إدخال المدونة الأول"، "Ian")
إدراج في التعليقات (المعرف، blog_id، التعليق، المؤلف) القيم (NULL، 1، 'التعليق على أول إدخال للمدونة'، 'Susan Norton')، (NULL، 1، 'التعليق على الإدخال الأول للمدونة'، 'Rose Wilson')
الكود أعلاه يحاكي في الواقع الموقف الذي علق فيه القراء سوزان وروز على مدونتنا الأولى. لنفترض الآن أننا نريد تحديث المدونة الأولى بمنشور آخر. وبطبيعة الحال، هذا الوضع ممكن.
في هذه الحالة، من أجل الحفاظ على اتساق قاعدة البيانات، يجب أيضًا تحديث جدول التعليقات وفقًا لذلك، إما يدويًا أو عن طريق تطبيق يعالج طبقة البيانات. في هذا المثال، سنستخدم أمر SQL لإكمال التحديث كما يلي:
تحديث معرف مجموعة المدونات = 2، العنوان = "عنوان إدخال المدونة الأول"، المحتوى = "محتوى إدخال المدونة الأول"، المؤلف = "John Doe" حيث المعرف = 1
تحديث التعليقات SET blog_id = 2 حيث blod_id = 1
كما ذكرنا من قبل، نظرًا لأنه تم تحديث محتوى عنصر بيانات المدونة الأولى، يجب أن يعكس جدول التعليقات هذا التغيير أيضًا. بالطبع، في الواقع، يجب إكمال عملية التحديث هذه في طبقة التطبيق وليس يدويًا، مما يعني أنه يجب تنفيذ هذا المنطق باستخدام لغة من جانب الخادم.
من أجل إكمال هذه العملية، يمكن لـ PHP استخدام عملية فرعية بسيطة، ولكن في الواقع، إذا تم استخدام قيود المفتاح الخارجي، فيمكن تفويض عملية تحديث جدول التعليقات إلى قاعدة البيانات.
كما ذكرنا سابقًا في المقالة، توفر جداول InnoDB MySQL دعمًا سلسًا لهذه الوظيفة. ولذلك، في الجزء الأخير سوف نستخدم قيود المفتاح الخارجي لإعادة إنشاء رمز المثال السابق.
التحديثات المتتالية لقاعدة البيانات
أدناه، سنقوم بإعادة هيكلة كود المثال السابق باستخدام قيود المفتاح الخارجي وجدول InnoDB (بدلاً من نوع MyISAM الافتراضي). للقيام بذلك، قم أولاً بإعادة تعريف نموذجي الجدولين بحيث يمكنهم استخدام محرك قاعدة بيانات محدد. للقيام بذلك، يمكنك استخدام كود SQL مثل ما يلي:
قم بإسقاط الجدول إذا كان موجودًا `test`.`blogs`;
إنشاء جدول `اختبار`.`مدونات` (
`id` INT(10) AUTO_INCREMENT غير الموقعة،
نص "العنوان" ،
"المحتوى" النص،
"المؤلف" VARCHAR(45) افتراضي فارغ،
مفتاح بريروز (`المعرف`)
) المحرك=InnoDB DEFAULT CHARSET=utf8;
قم بإسقاط الجدول إذا كان موجودًا `test`.`comments`;
إنشاء جدول `اختبار`.`تعليقات` (
`id` INT(10) AUTO_INCREMENT غير الموقعة،
`blog_id` INT(10) غير موقعة افتراضيًا فارغة،
نص "التعليق"،
"المؤلف" VARCHAR(45) افتراضي فارغ،
مفتاح بريروز (`المعرف`)،
المفتاح `blog_ind` (`blog_id`)،
القيد `comments_ibfk_1` المفتاح الخارجي (`blog_id`) المراجع `blogs` (`id`) في سلسلة التحديثات
) المحرك=InnoDB DEFAULT CHARSET=utf8;
الفرق الواضح بين الكود هنا والكود السابق هو أن الجدولين يستخدمان الآن محرك تخزين InnoDB، حتى يتمكنوا من دعم قيود المفتاح الخارجي. بالإضافة إلى ذلك، نحتاج أيضًا إلى الانتباه إلى الكود الذي يحدد جدول التعليقات:
القيد `comments_ibfk_1` المفتاح الخارجي (`blog_id`) المراجع `blogs` (`id`) في سلسلة التحديثات
في الواقع، يُعلم هذا البيان MySQL أنه عند تحديث جدول المدونات، يجب أيضًا تحديث قيمة المفتاح الخارجي blog_id في جدول التعليقات. بمعنى آخر، ما يتم فعله هنا هو السماح لـ MySQL بالحفاظ على سلامة قاعدة البيانات بطريقة متتالية. وهذا يعني أنه عند تحديث مدونة، يجب أن تعكس التعليقات المرتبطة بها هذا التغيير على الفور. والمهم هو تنفيذ هذه الوظيفة لم يتم ذلك في طبقة التطبيق.
لقد تم تعريف مثالين لجدولي MySQL. الآن، يعد تحديث هذين الجدولين أمرًا بسيطًا مثل تشغيل عبارة UPDATE، كما هو موضح أدناه:
"تحديث معرف مجموعة المدونات = 2، العنوان = "عنوان إدخال المدونة الأول"، المحتوى = "محتوى إدخال المدونة الأول"، المؤلف = "John Doe" حيث المعرف = 1"
كما ذكرنا سابقًا، لا نحتاج إلى تحديث جدول التعليقات لأن MySQL ستتعامل مع هذا الأمر تلقائيًا. بالإضافة إلى ذلك، يمكنك جعل MySQL لا تفعل شيئًا عند محاولة تحديث صف في جدول المدونات عن طريق إزالة الجزء "ON UPDATE" من الاستعلام أو تحديد "NO ACTION" و"RESTRICT". بالطبع، يمكنك أيضًا السماح لـ MySQL بالقيام بأشياء أخرى، والتي سيتم تقديمها في المقالات اللاحقة.
من خلال المقدمة أعلاه، أعتقد أن كل شخص لديه فهم واضح لكيفية استخدام قيود المفاتيح الخارجية جنبًا إلى جنب مع جداول InnoDB في MySQL. وبالطبع، يمكنك أيضًا كتابة تعليمات برمجية فورية لتعميق فهمك لوظيفة قاعدة البيانات المريحة هذه.