رفع خطای The DELETE / INSERT statement conflicted with the FOREIGN KEY constraint
یکی از خطاهای رایج هنگام کار با دو جدول مرتبط این است:
Msg 547, Level 16, State 0 The DELETE statement conflicted with the REFERENCE constraint "FK_Enrollment_Student". The conflict occurred in database "SchoolDB", table "dbo.Enrollment", column 'StudentId'.
و نسخهی دیگر آن هنگام درج:
Msg 547, Level 16, State 0 The INSERT statement conflicted with the FOREIGN KEY constraint "FK_Enrollment_Course". The conflict occurred in database "SchoolDB", table "dbo.Course", column 'CourseId'.
متن خطا را درست بخوان
| بخش پیام | یعنی |
|---|---|
The DELETE statement conflicted with the REFERENCE constraint | میخواهی ردیف والد را حذف کنی ولی فرزند دارد |
The INSERT statement conflicted with the FOREIGN KEY constraint | میخواهی فرزند را درج کنی ولی والد ندارد |
FK_Enrollment_Student | نام قید |
table ... column ... | جدول و ستونی که در آن تعارض پیدا شده (در DELETE: جدول فرزند، در INSERT: جدول والد) |
حالت ۱: خطا هنگام DELETE
DELETE FROM Student WHERE StudentId = 5; -- خطا: Enrollment فرزند دارد
اول ببین چه ردیفهایی به آن وابستهاند:
SELECT * FROM Enrollment WHERE StudentId = 5;
بسته به منطق کسبوکار یکی از راهحلهای زیر را انتخاب کن:
راهحل الف) ابتدا فرزندها را حذف کن
BEGIN TRANSACTION; DELETE FROM Enrollment WHERE StudentId = 5; DELETE FROM Student WHERE StudentId = 5; COMMIT;
راهحل ب) حذف آبشاری (ON DELETE CASCADE)
اگر ثبتنام بدون دانشجو بیمعنی است، قید را با CASCADE بازتعریف کن تا فرزندها خودکار حذف شوند:
ALTER TABLE Enrollment DROP CONSTRAINT FK_Enrollment_Student; ALTER TABLE Enrollment ADD CONSTRAINT FK_Enrollment_Student FOREIGN KEY (StudentId) REFERENCES Student(StudentId) ON DELETE CASCADE;
راهحل ج) حذف منطقی
برای دادههای مهم، ردیف را واقعاً پاک نکن؛ آن را غیرفعال علامت بزن:
ALTER TABLE Student ADD IsDeleted BIT NOT NULL DEFAULT 0; UPDATE Student SET IsDeleted = 1 WHERE StudentId = 5;
حالت ۲: خطا هنگام INSERT یا UPDATE
INSERT INTO Enrollment (StudentId, CourseId) VALUES (1, 99); -- CourseId=99 وجود ندارد
بررسی کن مقدار کلید خارجی در جدول والد هست یا نه:
SELECT * FROM Course WHERE CourseId = 99; -- خالی؟ پس درج مجاز نیست
راهحل: یا ابتدا والد را درج کن، یا مقدار درست را در ستون بگذار. اگر ستون اختیاری است میتوان NULL گذاشت.
پیدا کردن همهی جدولهایی که به یک جدول وابستهاند
SELECT fk.name AS ForeignKey, OBJECT_NAME(fk.parent_object_id) AS ChildTable, OBJECT_NAME(fk.referenced_object_id) AS ParentTable, fk.delete_referential_action_desc AS OnDelete FROM sys.foreign_keys fk WHERE fk.referenced_object_id = OBJECT_ID('dbo.Student');
پیدا کردن ردیفهای یتیم موجود
اگر قید را بعداً اضافه میکنی و خطا میگیری، احتمالاً دادهی نامعتبر داری. این کوئری فرزندان بدون والد را پیدا میکند:
SELECT e.* FROM Enrollment e LEFT JOIN Student s ON s.StudentId = e.StudentId WHERE s.StudentId IS NULL;
غیرفعال کردن موقت قید (با احتیاط)
ALTER TABLE Enrollment NOCHECK CONSTRAINT FK_Enrollment_Student; -- ... بارگذاری داده ... ALTER TABLE Enrollment WITH CHECK CHECK CONSTRAINT FK_Enrollment_Student; -- بررسی مجدد
چگونه از این خطا جلوگیری کنیم؟
- از ابتدا ترتیب درست را رعایت کن: اول والد، بعد فرزند؛ برای حذف برعکس.
- برای عملیات چندجدولی از تراکنش استفاده کن.
- برای حذفهای پرتکرار،
ON DELETE CASCADEیا حذف منطقی را در طراحی در نظر بگیر. - نام قیدها را معنادار انتخاب کن تا پیام خطا سریع قابلفهم باشد.
برای شناخت بهتر کلیدها، مقالهی تفاوت Primary Key، Unique و Foreign Key و درس Foreign Key را بخوان.
سوالات متداول دربارهی این موضوع
Msg 547 دقیقاً یعنی چه؟
یعنی عملیات (INSERT، UPDATE یا DELETE) قید کلید خارجی را نقض میکند: یا ردیفی را درج میکنی که والدش وجود ندارد، یا والدی را حذف میکنی که فرزند دارد.
آیا میتوانم قید را موقتاً غیرفعال کنم؟
فنی بله (NOCHECK CONSTRAINT)، اما توصیه نمیشود چون ردیفهای نامعتبر وارد میشوند و بعداً مشکلساز میشوند. فقط برای بارگذاریهای کنترلشده و با بررسی مجدد (WITH CHECK CHECK) استفاده کن.
ON DELETE CASCADE همیشه راهحل خوبی است؟
نه. CASCADE ممکن است ناخواسته دادههای زیادی را حذف کند. برای دادههای مهم (مثل سفارشهای مشتری) بهتر است حذف منطقی (IsDeleted) داشته باشی.
میخوای SQL را از پایه و با تمرین یاد بگیری؟
دورهی رایگان SQLFarsi را با Playground و پروژههای واقعی دنبال کن.
شروع یادگیری