بنناجح
المرحلة 1 · أساسيات الداتابيز الدرس 2 من 3

المفاتيح والعلاقات بين الجداول

إزاي الداتابيز تعرف إن الصف ده هو الصف ده، وإزاي الجداول بتشاور على بعض. ده الأساس اللي الـ JOIN كله مبني عليه، واللي من غيره هتكتب JOINs بتطلّع أرقام غلط.

في الدرس ده هتتعلم
  • تفرق بين Primary Key و Foreign Key، وتعرف كل واحد بيمنع إيه
  • تحدد نوع العلاقة بين أي جدولين: واحد لواحد، واحد لكتير، كتير لكتير
  • تفهم ليه علاقة كتير لكتير محتاجة جدول تالت في النص
  • تتوقع عدد الصفوف لما تمشي من جدول للتاني (الـ Grain)

الـ Primary Key: هوية الصف

عندك عميلين اسمهم Ahmed Hassan (روح جدول customers وشوف الصف 1 والصف 13). إزاي تفرق بينهم؟ مش بالاسم أكيد. كل جدول محتاج عمود (أو أكتر) قيمته مختلفة في كل صف، يبقى هو هوية الصف. ده اسمه Primary Key.

جرّب تضيف ماركة بنفس الـ id بتاع Dell:

id متكرر
INSERT INTO brands VALUES (1, 'Samsung', 'South Korea');

ودلوقتي بـ id جديد، وبعدها اعرض الجدول في نفس التشغيلة:

id جديد
INSERT INTO brands VALUES (7, 'Samsung', 'South Korea');
SELECT * FROM brands;

Samsung ظهرت. وشغّل SELECT * FROM brands; لوحدها بعدها، هتلاقيها اختفت: كل تشغيل بيترجع ROLLBACK في الآخر، علشان الداتا تفضل زي ما هي للكل.

طبيعي ولا متركّب؟

النوع يعني إيه مثال
Surrogate Key (مصطنع) رقم ملوش معنى غير إنه هوية id في كل جداولنا
Natural Key (طبيعي) قيمة ليها معنى في الحقيقة وفريدة الرقم القومي، الإيميل
Composite Key (مركّب) أكتر من عمود مع بعض هما الهوية (order_id, product_id) في order_items

الـ Foreign Key: الجداول بتشاور على بعض

الطلب محتاج يعرف هو بتاع أنهي عميل. بدل ما نكرر اسم العميل وإيميله ومدينته في كل طلب، بنخزّن رقمه بس: orders.customer_id. والعمود ده بيشاور على customers.id.

جرّب طلب لعميل رقم 999، وهو مش موجود:

عميل مش موجود
INSERT INTO orders VALUES (100, 999, '2024-11-01 10:00', 'pending', 'Cairo');

والعكس: امسح عميل عنده طلبات. الـ WHERE هنشرحها في المرحلة الجاية، دلوقتي اعرف إنها بتختار صف معين:

امسح عميل عنده طلبات
DELETE FROM customers WHERE id = 1;

الداتابيز رفضت، لأن لو العميل اتمسح، طلباته هتبقى بتشاور على حاجة مش موجودة (Orphan Rows). وده بالظبط اللي بيمنع داتا الشركة تتبوّظ مع الوقت.

أنواع العلاقات

تبص على أي جدولين وتسأل نفسك سؤالين: الصف الواحد هنا، يقابله كام صف هناك؟ والعكس؟

العلاقة المعنى في داتابيز الكورس
واحد لكتير (1 : N) العميل ليه طلبات كتير، والطلب لعميل واحد customers ← orders
الماركة ليها منتجات كتير brands ← products
كتير لكتير (N : M) الطلب فيه منتجات كتير، والمنتج في طلبات كتير orders ↔ products عن طريق order_items
مع نفسه (Self) الموظف ليه مدير، والمدير موظف برضه employees.manager_id ← employees.id
واحد لواحد (1 : 1) كل صف يقابله صف واحد بس مش موجود عندنا. مثال: users و user_settings

تتبّع عميل بعينك

اختار عميل، وشوف طلباته، وبعدها المنتجات اللي في كل طلب. ركّز في عدد الصفوف فوق كل عمود: هو ده اللي بيتغيّر لما تربط الجداول.

تتبّع عميلمحاكاة

جرّب Karim Mostafa: عميل موجود وملوش ولا طلب. وجرّب Ziad Nabil: عنده طلب ملغي، وطلب تاني فاضي مفيهوش منتجات. الحالتين دول هما بالظبط الفرق بين INNER JOIN و LEFT JOIN، اللي هنشرحه في مرحلة الربط بين الجداول.

ليه كتير لكتير محتاجة جدول تالت؟

الطلب ممكن يبقى فيه أكتر من منتج. طب نحط product_id في جدول orders؟ يبقى طلب فيه منتج واحد بس. نحط product_id_1 و product_id_2 و…؟ يبقى فيه حد أقصى، وكويري “مين اشترى المنتج ده” هتبقى كابوس.

الحل: جدول في النص، كل صف فيه = منتج جوه طلب:

جدول الربط
SELECT * FROM order_items;

وكمان فيه أعمدة مكانها الطبيعي هو العلاقة نفسها: quantity (اشترى كام واحد من المنتج ده في الطلب ده)، و unit_price (السعر وقت الشراء، اللي ممكن يختلف عن سعر المنتج النهارده: بص على الطلب 9).

سؤال✓

الـ Primary Key بتاع order_items هو (order_id, product_id) مع بعض. ده بيمنع إيه بالظبط؟

سؤال✓

العلاقة بين categories و products نوعها إيه؟

الـ Grain: الصف بيمثّل إيه؟

ده أهم مفهوم في الدرس كله، وهتسمعه كتير:

لما تمشي من جدول لجدول عن طريق علاقة واحد لكتير، الـ Grain بيتغيّر وعدد الصفوف بيزيد. عميل واحد بقى 4 طلبات، والـ 4 طلبات بقوا 5 صفوف في order_items. ولو جمعت أي حاجة من جدول customers بعد الربط (زي عدد العملاء)، هتلاقي الرقم متضاعف. وده أشهر سبب للأرقام الغلط في التقارير.

سؤال✓

ربطت customers بـ orders بـ order_items. عميل عنده 3 طلبات، وكل طلب فيه منتجين. العميل ده هيظهر في كام صف؟

اتمرّن

لسه ما اتعلمناش الربط ولا الفلترة، فالتمارين دي عن قراية الجداول صح.

تمرين تطبيقي

سهل✓ اتحل

اعرض لكل طلب: رقمه (id)، ورقم العميل اللي طلبه، والحالة بتاعته.

تمرين تطبيقي

سهل✓ اتحل

اعرض كل موظف بالـ id والاسم، ورقم المدير بتاعه. مين الموظف اللي manager_id بتاعه NULL؟ وده منطقي ليه؟

💡 تلميح

العمود اللي بيشاور على المدير اسمه manager_id.

تمرين تطبيقي

متوسط✓ اتحل

لكل صف في order_items: اعرض رقم الطلب، ورقم المنتج، وإجمالي السطر (الكمية × سعر الوحدة) في عمود اسمه line_total. ده الـ Grain بتاع الجدول: منتج جوه طلب.

💡 تلميح

الحساب بيحصل جوه الـ SELECT: quantity * unit_price، وسمّيه بـ AS line_total.