نوشتنِ FOREIGN KEY در ساختارِ SQLite بهتنهایی نشان نمیدهد قید در اتصالِ جاری اجرا میشود. وضعیتِ PRAGMA foreign_keys را صریح تنظیم و دوباره بخوانید، سپس رفتارِ درج و حذف را روی دادهٔ آزمایشی ببینید. این صفحه دو اتصالِ مستقل با یک schema دارد؛ در یکی orphan پذیرفته و در دیگری همان درج رد میشود.
تعریفِ رابطه با اجرایِ قید فرق دارد
جدولِ parent کلیدِ صحیحِ۱را دارد. childستونِ pidرا به همین کلید ارجاع میدهد و برای حذف، ON DELETE NO ACTIONرا صریح نوشتهایم؛ قیدِ نمونه فوری است، نه deferred. شناسهٔ۹۹۹در parentوجود ندارد. درجِ فرزند با pid=۹۹۹همان خطایی است که قصد داریم رفتارِ دو اتصال را با آن مقایسه کنیم.
مستندِ SQLite شرایطِ پشتیبانیِ build و فعالسازیِ runtime را جدا توضیح میدهد. به مقدارِ پیشفرضِ یک نسخه تکیه نمیکنیم: برای اتصالِ اول OFFو دومیONمیگذاریم و خواندنِ PRAGMAباید بهترتیب۰و۱بدهد. اگر پشتیبانی یا نتیجهٔ لازم موجود نباشد، assertِ تمرین میایستد؛ موفقیتِ فرضی نمینویسیم.
دو اتصال، همان ورودیِ نامعتبر
هر دو پایگاه :memory:هستند و جدولها و ورودیِ اصلی یکساناند. در حالتِ OFF،درجِ childِ۱۰با pidِ۹۹۹پذیرفته میشود؛ وجودِ متنِ REFERENCESدر schemaجلوی آن را نگرفته است. در حالتِ ON،همان دستور IntegrityErrorمیدهد و ردیفِ orphanوارد نمیشود. این تفاوت واقعاً اجرا و در JSONثبت شده است.
| عملِ آموزشی | قید خاموش | قید روشن |
|---|---|---|
| فرزند با والدِ۹۹۹ِ ناموجود | پذیرش | ردِ قید |
| فرزند با والدِ۱ِ موجود | پذیرش | پذیرش |
| حذفِ والدِ۱ِ دارای فرزند | پذیرش | رد در قراردادِ NO ACTIONِ فوری |
| pid=NULLدر ستونِ nullable | پذیرش | پذیرش؛ NOT NULLجداست |
NULL با شمارهٔ والدِ ناموجود یکی نیست
در childِ نمونه، pidمیتواند NULLباشد؛ قیدِ کلیدِ خارجی برای این حالت الزام به وجودِ یک والدِ متناظر ندارد. بنابراین ردیفِ۱۱با pid=NULLدر هر دو اتصال پذیرفته میشود. اگر دامنه میگوید هر فرزند حتماً والد لازم دارد، قیدِ NOT NULLهم باید جدا و مطابقِ طرح تعریف شود.
جدولِ required_childهمین تفاوت را نشان میدهد: ستونِ pidدر آن NOT NULLاست و درجِ مقدارِ تهی در هر دو حالت خطا میدهد. از ردِ آن نمیتوان نتیجه گرفت FOREIGN KEYروشن بوده است؛ قیدِ دیگری خطا را ایجاد کرده است. متنِ خطای ثبتشده کمک میکند علتها را از هم جدا بخوانیم.
تغییرِ PRAGMA در میانهٔ تراکنش را امتحان کنیم
روی اتصالِ روشن BEGINمیزنیم و بعد PRAGMA foreign_keys=OFFمیفرستیم. queryِ وضعیت هنوز۱برمیگرداند؛ در میانهٔ تراکنشِ چنددستوری این تغییر اثر نکرده است. سپس ROLLBACKمیزنیم و بیرون از تراکنش دوبارهOFFمیفرستیم؛ این بار خواندنِ وضعیت۰است.
در فایل، transaction modeاز ابتدا با isolation_level=Noneروشن شده و BEGINوالد صریح است. این کنترل برای فهمیدنِ زمانِ اعمالِ تنظیم است؛ راهنمای خاموشکردنِ قیدِ پایگاهِ عملیاتی برای جاانداختنِ دادهٔ خراب نیست. خاموشبودنِ قید، رابطهٔ تجاریِ نامعتبر را معتبر نمیکند.
این آزمایش همهٔ سیاستهای حذف را نمیگوید
CASCADE،SET NULL،RESTRICT،قیدِ ترکیبی و deferredدر نمونه پیاده نشدهاند. رفتارِ حذفِ جدولِ بالا متعلق به NO ACTIONِ فوریِ همین schemaاست؛ آن را به تمامِ تنظیمات تعمیم ندهید. خودِ برقرارشدنِ رابطه هم نمیگوید متن تازه است، کاربر اجازهٔ دیدنِ والد را دارد یا ثبت از نظرِ کسبوکار درست است.
پیوندِ رکورد به منبع میتواند منبعِ ناموجود وhashِ قدیمی را در auditپیدا کند؛ اینجا بهطور مستقل اجرایِ runtimeِ قید را میسنجیم. پیشبررسیِ ورودِ CSV هم قراردادِ فیلد را میپرسد و جایِ آزمونِ اتصالِ پایگاه را نمیگیرد.
برای اتصالِ واقعی چه شاهدی بخواهیم؟
وضعیتِ هر connectionِ برنامه، schemaو رفتارِ خطای همان مسیر باید معلوم باشد؛ روشنبودن در یک پنجرهٔ مدیریت، اتصالِ دیگری را تأیید نمیکند. برای مهاجرتِ داده یا تغییرِ قید به نسخهٔ کاری، برنامهٔ برگشت و مسئولِ مجاز نیاز است. این تمرین فقط سه جدولِ حافظهایِ ساختهشده را تغییر میدهد و اصلِ دادهای ندارد که منتقل شود.
اگر برای راهکارِ متصلِ اپرا اطلاعاتِ درخواست به سامانهٔ دیگر میرود، قراردادِ شناسه و خطایِ رابطه را در گفتوگو با تیم فنی روشن کنید. استفاده ازSQLite،نامِ جدول یا پشتیبانیِ رفتارِ خاص در محصول از این مثال نتیجه نمیشود؛ نمونه معیارِ یک بررسیِ فنی است.
فایل تمرین و منبع
کدِ کاملِ محلی و کنترلهای اصیلِ تمرین · دادهٔ ساختگی و خروجیِ اجرای واقعی
منابع در 2026-11-04 خوانده شدند؛ این تاریخ، تاریخ انتشار منبع نیست. مثالها و فایلهای این راهنما آموزشی و ساختهٔ تحریریهاند.