إصلاح خطأ "a foreign key constraint fails" عند استيراد ملف SQL dump

يتوقف الاستيراد في منتصف الطريق مع رسالة Cannot add or update a child row: a foreign key constraint fails. البيانات سليمة — المشكلة في ترتيب عبارات INSERT. إليك سبب حدوث ذلك وثلاث طرق لإصلاحه.

تُصدِّر قاعدة بيانات وتحاول تحميلها في نسخة جديدة، فيُطلق MySQL هذا الخطأ:

ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`app`.`orders`, CONSTRAINT `orders_user_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`))

أو على PostgreSQL:

ERROR: insert or update on table "orders" violates foreign key constraint "orders_user_id_fkey"
DETAIL: Key (user_id)=(42) is not present in table "users".

لماذا يحدث هذا

يقول المفتاح الخارجي إن “كل orders.user_id يجب أن يشير إلى users.id موجود.” وعندما يُدرِج الـ dump جدول orders قبل جدول users، لا يكون الصف الأب المُشار إليه موجودًا بعد، فترفض قاعدة البيانات الصف.

تكتب معظم أدوات الـ dump الجداول أبجديًا أو حسب ترتيب الإنشاء — لا حسب ترتيب اعتماد المفاتيح الخارجية. لذا كثيرًا ما يأتي orders (الابن) قبل users (الأب)، فيفشل الاستيراد.

الحل الأول: إعادة ترتيب عبارات INSERT حسب التبعية (الحل الصحيح)

الحل النظيف هو إدراج كل جدول أب قبل أبنائه. وهذا يعني تحليل المفاتيح الخارجية إلى رسم بياني للتبعيات وتحميل الجداول بـالترتيب الطوبولوجي: users → orders → order_items.

القيام بذلك يدويًا في ملف بحجم عدة غيغابايت أمر مؤلم — عليك تتبّع كل FOREIGN KEY عبر عبارات CREATE TABLE وALTER TABLE وقص/لصق كتل INSERT الضخمة بالتسلسل الصحيح.

الحل الثاني: تعطيل فحص المفاتيح الخارجية أثناء الاستيراد

بالنسبة لملف dump كامل ومتّسق مسبقًا يمكنك بأمان تخطّي التحقق أثناء التحميل. على MySQL، أحِط الملف هكذا:

-- MySQL
SET FOREIGN_KEY_CHECKS = 0;
-- ... جميع عبارات INSERT الخاصة بك ...
SET FOREIGN_KEY_CHECKS = 1;

على PostgreSQL، أجّل القيود داخل معاملة (transaction):

-- PostgreSQL
BEGIN;
SET CONSTRAINTS ALL DEFERRED;
-- ... جميع عبارات INSERT الخاصة بك ...
COMMIT;

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

الحل الثالث: افعل الأمرين تلقائيًا باستخدام DumpCleaner

يقرأ DumpCleaner الـ dump، ويستخرج كل مفتاح خارجي من عبارات CREATE TABLE وALTER TABLE، ويبني رسم التبعيات، ويُصدِّر ملفًا نظيفًا يُستورَد ببساطة:

  1. اسحب ملف .sql إلى DumpCleaner (MySQL أو MariaDB أو PostgreSQL أو SQLite أو MS SQL).
  2. اختر ترتيب INSERT ← حسب التبعيات (مع مراعاة المفاتيح الخارجية). تُوضَع الجداول الأب قبل الأبناء تلقائيًا عبر الترتيب الطوبولوجي.
  3. اختياريًا فعِّل تعطيل فحص المفاتيح الخارجية — سيُحيط المخرجات بالعبارة الصحيحة لقاعدة بياناتك.
  4. صدِّر. استورد الملف الجديد — واختفى خطأ القيد.

كما يكتشف المفاتيح الخارجية الدائرية ويُحذّرك بدلًا من إنتاج ترتيب مستحيل.

لا مزيد من جراحة INSERT اليدوية

يعيد DumpCleaner ترتيب عبارات الإدراج حسب تبعية المفاتيح الخارجية على ملفات بأي حجم — بالبثّ (streaming) وباستهلاك ذاكرة ثابت. تطبيق أصلي لنظامي macOS وiPadOS، بشراء لمرة واحدة.

التنزيل من App Store

الأسئلة الشائعة

لماذا يحدث خطأ “a foreign key constraint fails” عند الاستيراد؟

يشير صف ابن إلى صف أب لم يُدرَج بعد، لأن الـ dump يرتّب عبارات INSERT أبجديًا أو حسب وقت الإنشاء بدلًا من ترتيبها حسب تبعية المفاتيح الخارجية.

هل من الآمن تعطيل فحص المفاتيح الخارجية أثناء الاستيراد؟

نعم، بالنسبة لملف dump كامل ومتّسق. أنت تتخطّى فقط التحقق من بيانات كانت صالحة أصلًا في قاعدة البيانات المصدر. أعِد تفعيل الفحص بعد ذلك.

كيف أعيد ترتيب عبارات INSERT دون تحرير الملف يدويًا؟

يكتشف DumpCleaner المفاتيح الخارجية، ويبني رسم تبعيات، ويُصدِّر عبارات INSERT بالترتيب الطوبولوجي — بحيث تُحمَّل الجداول الأب دائمًا قبل الأبناء.

هل يعمل هذا مع PostgreSQL أيضًا؟

نعم. يدعم DumpCleaner مخرجات pg_dump ويمكنه إصدار SET CONSTRAINTS ALL DEFERRED إضافةً إلى عبارات الإدراج المرتّبة حسب التبعية.

استورد ملفات الـ dump دون صداع المفاتيح الخارجية

اسحب، وأعِد الترتيب حسب التبعيات، وصدِّر. يتولّى DumpCleaner المفاتيح الخارجية ليعمل استيرادك ببساطة.

التنزيل من App Store