تخطَّ إلى المحتوى

ربط جداول المتجر في MySQL: JOIN والمفاتيح الأجنبية

الدرس 7 من 12 · MySQL

متجرنا الآن فيه منتجات، لكنه بلا عملاء ولا طلبات! في هذا الدرس سنكمل قاعدة البيانات بجدولين جديدين مترابطين، وستتعلم كيف تحمي MySQL هذه الروابط بالمفاتيح الأجنبية، وكيف تجمع البيانات من الجداول الثلاثة في تقرير واحد بـ JOIN.

CREATE TABLE customers (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
city VARCHAR(50)
);
INSERT INTO customers (name, city) VALUES
('سارة', 'القاهرة'), ('عمر', 'الإسكندرية'), ('منى', 'طنطا'), ('كريم', 'القاهرة');

جدول الطلبات بمفاتيح أجنبية

Section titled “جدول الطلبات بمفاتيح أجنبية”
CREATE TABLE orders (
id INT AUTO_INCREMENT PRIMARY KEY,
customer_id INT NOT NULL,
product_id INT NOT NULL,
quantity INT NOT NULL DEFAULT 1,
order_date DATE NOT NULL,
FOREIGN KEY (customer_id) REFERENCES customers(id) ON DELETE CASCADE,
FOREIGN KEY (product_id) REFERENCES products(id)
);
INSERT INTO orders (customer_id, product_id, quantity, order_date) VALUES
(1, 1, 1, '2026-10-05'),
(1, 4, 1, '2026-10-12'),
(2, 2, 2, '2026-10-15'),
(3, 5, 3, '2026-10-21'),
(1, 6, 2, '2026-11-01'),
(2, 1, 1, '2026-11-03');

كل طلب يشير إلى عميل وإلى منتج برقميهما. و FOREIGN KEY ... REFERENCES تعلن هذه العلاقة رسمياً، فتراقبها MySQL.

الربط الداخلي: من اشترى ماذا؟

Section titled “الربط الداخلي: من اشترى ماذا؟”
SELECT c.name AS customer, p.name AS product, o.quantity
FROM orders o
JOIN customers c ON o.customer_id = c.id
JOIN products p ON o.product_id = p.id
ORDER BY o.id;

الناتج:

customer product quantity
سارة كتاب البرمجة للمبتدئين 1
سارة آلة حاسبة علمية 1
عمر سماعة لاسلكية 2
منى قصص الأطفال 3
سارة مجموعة أقلام 2
عمر كتاب البرمجة للمبتدئين 1

انطلقنا من جدول الطلبات، ولكل طلب جلبنا اسم العميل واسم المنتج المطابقين. والأسماء المختصرة o و c و p توفر الكتابة. ولاحظ AS customer و AS product: كان لدينا عمودان باسم name من جدولين مختلفين، فأعطينا كلاً منهما اسماً واضحاً في النتيجة.

الربط اليساري: كل العملاء

Section titled “الربط اليساري: كل العملاء”

كم طلباً لكل عميل، بمن فيهم من لم يطلب شيئاً؟

SELECT c.name, COUNT(o.id) AS orders_count
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id
GROUP BY c.id, c.name
ORDER BY c.id;

الناتج:

name orders_count
سارة 3
عمر 2
منى 1
كريم 0

مع INNER JOIN كان كريم سيختفي، لأنه لا طلبات له. أما LEFT JOIN فتُبقي كل العملاء. (و GROUP BY و COUNT تجميع ستتعمق فيه في الدرس القادم.)

والعملاء الذين لم يطلبوا أبداً، وهم هدف مناسب لرسالة «خصم لأول طلب»:

SELECT c.name
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id
WHERE o.id IS NULL;

الناتج: كريم.

المفاتيح الأجنبية تحمي البيانات

Section titled “المفاتيح الأجنبية تحمي البيانات”

طلب لعميل غير موجود:

INSERT INTO orders (customer_id, product_id, order_date) VALUES (99, 1, '2026-11-05');

الناتج: ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails ...

حذف منتج له طلبات:

DELETE FROM products WHERE id = 1;

الناتج: ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails ...

رفضت MySQL حذف «كتاب البرمجة» لأن هناك طلبات تشير إليه. لو حُذف، لأصبحت هذه الطلبات تشير لمنتج غير موجود، وفسد سجل المبيعات.

ON DELETE CASCADE: الحذف المتسلسل

Section titled “ON DELETE CASCADE: الحذف المتسلسل”

لاحظ أن مفتاح العميل في جدول الطلبات كان مختلفاً: ON DELETE CASCADE. معناه: إذا حُذف العميل، احذف طلباته تلقائياً معه:

SELECT COUNT(*) FROM orders WHERE customer_id = 3; -- 1
DELETE FROM customers WHERE id = 3;
SELECT COUNT(*) FROM orders WHERE customer_id = 3; -- 0

حُذفت منى، وحُذف طلبها معها تلقائياً.

الخيار عند حذف الصف الأب
بدون خيار (الافتراضي) يُرفض الحذف إذا كانت هناك صفوف مرتبطة
ON DELETE CASCADE تُحذف الصفوف المرتبطة معه
ON DELETE SET NULL تصبح الإشارة NULL في الصفوف المرتبطة

تخيّل الفرق: CASCADE مثل هدم مبنى فتُزال معه كل لافتاته، والافتراضي مثل قانون يمنع الهدم ما دام فيه سكان.

  1. اعرض كل الطلبات مع اسم العميل، واسم المنتج، وتاريخ الطلب، مرتبة من الأحدث.
  2. اعرض طلبات عملاء القاهرة فقط.
  3. اعرض المنتجات التي لم يطلبها أحد (تلميح: ابدأ FROM products مع LEFT JOIN).
  4. أنشئ جدول reviews لتقييمات المنتجات بمفتاح أجنبي للمنتج مع ON DELETE CASCADE، وأضف تقييمين.
  • أعمدة بنفس الاسم دون أسماء مستعارة: نتيجة فيها عمودان name لا تعرف أيهما لمن. استخدم AS.
  • إنشاء جدول الطلبات قبل العملاء: المفتاح الأجنبي يحتاج الجدول المشار إليه موجوداً أولاً.
  • CASCADE في كل مكان: قد تفقد بيانات مهمة بحذف واحد.

سؤال

ماذا يحدث عند حذف عميل إذا كان المفتاح الأجنبي في جدول الطلبات معرّفاً بـ ON DELETE CASCADE؟

سؤال

لماذا رفضت MySQL حذف منتج له طلبات مسجلة؟

  • FOREIGN KEY ... REFERENCES يربط الجداول رسمياً، ويدعمه محرك InnoDB الافتراضي.
  • MySQL ترفض إضافة طلب لعميل غير موجود، أو حذف منتج له طلبات.
  • ON DELETE CASCADE يحذف الصفوف المرتبطة تلقائياً، فاستخدمه بحذر.
  • INNER JOIN للمتطابق فقط، و LEFT JOIN مع IS NULL لإيجاد العملاء بلا طلبات.
  • الأسماء المستعارة AS ضرورية عند تشابه أسماء الأعمدة بين الجداول.