المفاتيح الأجنبية وتعديل الجداول
كيف تحمي العلاقات بين الجداول، وكيف تطوّر الجداول بعد امتلائها بالبيانات؟ ⏱ نحو 40 دقيقة.
- تشرح كيف يحمي المفتاح الأجنبي العلاقة من الإدخال والحذف الخاطئين.
- تختار سلوك
ON DELETEالمناسب:RESTRICTأوCASCADEأوSET NULL. - تعدّل بنية جدول موجود بـ
ALTER TABLE. - تضيف مفتاحًا أجنبيًّا إلى جدول فيه بيانات.
١المفتاح الأجنبي يحمي العلاقة
REFERENCES يربط العمود بمفتاح جدول آخر، فيمنع شيئين: إدخال قيمة غير موجودة في الجدول الأصل، وحذف صف في الأصل تعتمد عليه صفوف أخرى.
INSERT INTO orders (customer_id, order_date) VALUES (99, current_date);INSERT INTO orders (customer_id, order_date) VALUES (8, current_date);SELECT id, customer_id, status FROM orders WHERE customer_id = 8;
| id | customer_id | status |
|---|---|---|
| 13 | 8 | new |
العميل 99 غير موجود، فرُفض الطلب. والعميل 8 (ماجد) موجود.
DELETE FROM customers WHERE id = 1;سارة لها طلبات، فلا يمكن حذفها. هذا السلوك الافتراضي: NO ACTION (ومثله RESTRICT).
٢ماذا يحدث عند حذف الأصل؟ ON DELETE
| الخيار | عند حذف صف الأصل | مثال مناسب |
|---|---|---|
RESTRICT / NO ACTION | يُرفض الحذف (الافتراضي) | عميل له طلبات |
CASCADE | تُحذف الصفوف التابعة معه | بنود الطلب تُحذف مع الطلب |
SET NULL | يصبح المفتاح الأجنبي NULL | منتج حُذف تصنيفه فيبقى بلا تصنيف |
SET DEFAULT | يأخذ القيمة الافتراضية | نقل إلى «تصنيف عام» |
في قاعدة المتجر بنود الطلب معرّفة بـ ON DELETE CASCADE. احذف الطلب 1 وراقب بنوده:
SELECT count(*) AS قبل FROM order_items WHERE order_id = 1;DELETE FROM orders WHERE id = 1;SELECT count(*) AS بعد FROM order_items WHERE order_id = 1;
| قبل |
|---|
| 2 |
| بعد |
|---|
| 0 |
حُذف البندان مع الطلب تلقائيًّا. CASCADE قوي، فاستخدمه حيث لا معنى للتابع دون أصله فقط.
٣تعديل جدول موجود: ALTER TABLE
الجداول تتطور مع النظام. ALTER TABLE يعدّل البنية دون حذف البيانات:
ALTER TABLE products ADD COLUMN discount numeric(5,2) NOT NULL DEFAULT 0;ALTER TABLE products ADD CONSTRAINT discount_range CHECK (discount BETWEEN 0 AND 50);ALTER TABLE products RENAME COLUMN stock TO qty_in_stock; SELECT id, name, qty_in_stock, discount FROM products WHERE id <= 3;
| id | name | qty_in_stock | discount |
|---|---|---|---|
| 1 | حاسب محمول | 7 | 0 |
| 2 | جهاز لوحي | 12 | 0 |
| 3 | شاشة 27 بوصة | 4 | 0 |
العمود الجديد أخذ القيمة الافتراضية 0 في كل الصفوف القديمة. و NOT NULL على عمود جديد يحتاج DEFAULT، وإلا رُفض لأن الصفوف القديمة ستكون فارغة.
| العملية | الأمر |
|---|---|
| إضافة عمود | ALTER TABLE t ADD COLUMN c type; |
| حذف عمود | ALTER TABLE t DROP COLUMN c; |
| إعادة تسمية عمود | ALTER TABLE t RENAME COLUMN a TO b; |
| تغيير النوع | ALTER TABLE t ALTER COLUMN c TYPE numeric(10,2); |
| إضافة قيد | ALTER TABLE t ADD CONSTRAINT n CHECK (…); |
| إضافة مفتاح أجنبي | ALTER TABLE t ADD FOREIGN KEY (c) REFERENCES p(id); |
توقّع وتحقّق
ما السلوك الافتراضي عند حذف صف أصل له صفوف تابعة؟
أنسب خيار لبنود الطلب عند حذف الطلب:
لتغيير اسم عمود نكتب:
في Oracle يُغيَّر نوع العمود بـ:
الخلاصة
REFERENCESيمنع الإشارة إلى غير موجود، ويمنع حذف أصل له توابع.ON DELETE: RESTRICTيرفض، وCASCADEيحذف التوابع، وSET NULLيفصلها.ALTER TABLEيضيف ويحذف ويعيد تسمية الأعمدة والقيود دون فقد البيانات.- عمود
NOT NULLجديد يحتاجDEFAULT.
تحدٍّ برمجي: الموردون والمنتجات
متقدمأضف إلى المتجر جدول الموردين، واربط المنتجات به، بحيث إذا حُذف مورد بقيت منتجاته بلا مورد.
- جدول
suppliers (id int IDENTITY PK, name text NOT NULL)وأضف إليه موردًا واحدًا على الأقل. - عمود جديد
supplier_idفي products، مفتاح أجنبي إلى suppliers معON DELETE SET NULL. - اجعل مورد المنتجات من 1 إلى 3 هو المورد رقم 1.
👀 اعرض الناتج المطلوب
- الكود يعمل بلا أخطاء
- المنتجات 1–3 مرتبطة بالمورد 1
- مورد غير موجود يُرفض
- حذف المورد يجعل منتجاته بلا مورد
- ON DELETE SET NULL
💡 تلميح 1
ALTER TABLE products ADD COLUMN supplier_id int REFERENCES suppliers(id) ON DELETE SET NULL;💡 تلميح 2
UPDATE products SET supplier_id = 1 WHERE id BETWEEN 1 AND 3;حاول بنفسك أولًا؛ للمسألة أكثر من حلّ صحيح.
CREATE TABLE suppliers ( id int GENERATED ALWAYS AS IDENTITY PRIMARY KEY, name text NOT NULL);INSERT INTO suppliers (name) VALUES ('مؤسسة النور'); ALTER TABLE products ADD COLUMN supplier_id int REFERENCES suppliers(id) ON DELETE SET NULL; UPDATE products SET supplier_id = 1 WHERE id BETWEEN 1 AND 3;