المفتاح الأجنبي وربط الجداول
العمود الذي يربط جدولًا بآخر ويحرس صحة الربط. ⏱ نحو 25 دقيقة.
- تعرّف المفتاح الأجنبي والجدول الأب والابن.
- تكتب المفتاح الأجنبي بطريقتيه.
- تفسّر رفض الإدخال والحذف حفاظًا على التكامل المرجعي.
- تختار بين
RESTRICTوCASCADEوSET NULL.
١ما المفتاح الأجنبي؟
المفتاح الأجنبي Foreign Key: عمود في جدول يحمل قيمة المفتاح الأساسي لصف في جدول آخر. به تتحقق العلاقة التي رسمناها في المخطط.
staff.dept_id مفتاح أجنبي يشير إلى depts.id. الجدول الذي فيه المفتاح الأساسي (depts) يسمى الأب Parent، والذي فيه المفتاح الأجنبي (staff) يسمى الابن Child.
٢كتابته بطريقتين
قصيرة بجانب العمود، أو مفصّلة في سطر مستقل بعد الأعمدة (وهي أوضح، وتلزم حين يكون المفتاح من عمودين):
-- 1) بجانب العمودdept_id INTEGER REFERENCES depts(id) -- 2) في سطر مستقلdept_id INTEGER,FOREIGN KEY (dept_id) REFERENCES depts(id)
CREATE TABLE depts ( id INTEGER PRIMARY KEY, name TEXT NOT NULL);CREATE TABLE staff ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, dept_id INTEGER REFERENCES depts(id));INSERT INTO depts VALUES (1, 'المبيعات'), (2, 'المستودع');INSERT INTO staff VALUES (10, 'سالم', 1), (11, 'منيرة', 1), (12, 'بدر', 2);SELECT * FROM staff;
| id | name | dept_id |
|---|---|---|
| 10 | سالم | 1 |
| 11 | منيرة | 1 |
| 12 | بدر | 2 |
لاحظ الترتيب: أنشأنا جدول الأب depts أولًا، ثم الابن. ولا يمكن أن يشير المفتاح الأجنبي إلى جدول لم يُنشأ بعد.
٣حارس التكامل المرجعي
المفتاح الأجنبي يرفض أمرين:
1) ابن يشير إلى أب غير موجود:
CREATE TABLE depts ( id INTEGER PRIMARY KEY, name TEXT NOT NULL);CREATE TABLE staff ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, dept_id INTEGER REFERENCES depts(id));INSERT INTO depts VALUES (1, 'المبيعات'), (2, 'المستودع');INSERT INTO staff VALUES (10, 'سالم', 1), (11, 'منيرة', 1), (12, 'بدر', 2);INSERT INTO staff VALUES (13, 'نوف', 7);
لا يوجد قسم رقمه 7.
2) حذف أب له أبناء:
CREATE TABLE depts ( id INTEGER PRIMARY KEY, name TEXT NOT NULL);CREATE TABLE staff ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, dept_id INTEGER REFERENCES depts(id));INSERT INTO depts VALUES (1, 'المبيعات'), (2, 'المستودع');INSERT INTO staff VALUES (10, 'سالم', 1), (11, 'منيرة', 1), (12, 'بدر', 2);DELETE FROM depts WHERE id = 1;
قسم المبيعات فيه سالم ومنيرة. لو حُذف لبقيا يشيران إلى قسم لا وجود له؛ ويسمى هذا «سجلًّا يتيمًا» Orphan.
٤ماذا يحدث للأبناء عند حذف الأب؟
بدل الرفض، يمكن أن نحدد ماذا يحدث للأبناء، بإضافة ON DELETE إلى المفتاح الأجنبي:
| الخيار | عند حذف الأب | مثال مناسب |
|---|---|---|
RESTRICT (أو بلا خيار) | يرفض الحذف. | لا نحذف عميلًا له فواتير. |
CASCADE | يحذف الأبناء معه. | حذف الطلب يحذف أصنافه. |
SET NULL | يجعل المفتاح الأجنبي في الأبناء NULL. | حذف القسم يترك الموظف بلا قسم. |
CREATE TABLE depts ( id INTEGER PRIMARY KEY, name TEXT NOT NULL);CREATE TABLE staff ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, dept_id INTEGER REFERENCES depts(id) ON DELETE SET NULL);INSERT INTO depts VALUES (1, 'المبيعات'), (2, 'المستودع');INSERT INTO staff VALUES (10, 'سالم', 1), (11, 'منيرة', 1), (12, 'بدر', 2);DELETE FROM depts WHERE id = 1;SELECT * FROM staff;
| id | name | dept_id |
|---|---|---|
| 10 | سالم | NULL |
| 11 | منيرة | NULL |
| 12 | بدر | 2 |
CREATE TABLE invoices (id INTEGER PRIMARY KEY, total REAL);CREATE TABLE invoice_lines ( invoice_id INTEGER REFERENCES invoices(id) ON DELETE CASCADE, item TEXT);INSERT INTO invoices VALUES (1, 50), (2, 30);INSERT INTO invoice_lines VALUES (1, 'قهوة'), (1, 'كيك'), (2, 'شاي'); DELETE FROM invoices WHERE id = 1;SELECT * FROM invoice_lines;
| invoice_id | item |
|---|---|
| 2 | شاي |
حذفنا الفاتورة 1، فحُذف سطراها تلقائيًّا، وبقي سطر الفاتورة 2.
٥المفتاح الأجنبي في متجر الواحة
في «متجر الواحة» ثلاثة مفاتيح أجنبية. اضغط 🗂 الجداول في المحرر لتراها، أو جرّب أن تحذف منتجًا بيع من قبل:
DELETE FROM products WHERE id = 1;DELETE FROM products WHERE id = 4;التمر السكري (رقم 4) لم يُطلب قط، فلا أبناء له، فحُذف.
توقّع وتحقّق
المفتاح الأجنبي يشير إلى:
حذف الطلب يحذف أصنافه معه. الخيار:
الصف الذي يشير إلى أب محذوف يسمى:
Orphan.أي الجدولين يُنشأ أولًا؟
الخلاصة
- المفتاح الأجنبي عمود في الابن يحمل المفتاح الأساسي للأب.
REFERENCESأب(عمود)، أوFOREIGN KEY(عمود)REFERENCESأب(عمود).- يرفض ابنًا لأب غير موجود، ويرفض حذف أب له أبناء.
ON DELETE CASCADEيحذف الأبناء، وSET NULLيتركهم بلا أب.
تحدٍّ برمجي: المؤلفون وكتبهم
متوسطأنشئ جدولين مترابطين: المؤلفين وكتبهم. إذا حُذف المؤلف تُحذف كتبه معه.
authors: العمودانid(مفتاح أساسي) وname(إلزامي).books: الأعمدةid(مفتاح أساسي) وtitle(إلزامي) وauthor_id.author_idإلزامي، ومفتاح أجنبي يشير إلى المؤلفين، مع الحذف المتسلسل.
👀 اعرض النتيجة المطلوبة
| COUNT(*) |
|---|
| 0 |
- الكود يعمل بلا أخطاء
- يقبل مؤلفًا
- يقبل كتابًا لمؤلف موجود
- يرفض كتابًا لمؤلف غير موجود
- يرفض كتابًا بلا مؤلف
- حذف المؤلف يحذف كتبه
💡 تلميح 1
💡 تلميح 2
FOREIGN KEY (author_id) REFERENCES authors(id) ON DELETE CASCADEحاول بنفسك أولًا؛ للمسألة أكثر من حلّ صحيح.
CREATE TABLE authors ( id INTEGER PRIMARY KEY, name TEXT NOT NULL); CREATE TABLE books ( id INTEGER PRIMARY KEY, title TEXT NOT NULL, author_id INTEGER NOT NULL, FOREIGN KEY (author_id) REFERENCES authors(id) ON DELETE CASCADE);