SQL: أنواع الانضمامات موضحة

تخيّل أنك تدير مقهى وتحتاج إلى التعامل مع ثلاث حالات يوميًا: العملاء الأعضاء الذين قاموا بمشتريات (كلا الجدولين بهما بيانات)، أعضاء لم يقوموا بمشتريات (الجدول الأيسر به بيانات، الأيمن لا)، وعملاء اشتروا لكنهم لم يسجلوا كأعضاء (الجدول الأيمن به بيانات، الأيسر لا). يوفر SQL أنواعًا مختلفة من JOINs للتعامل بدقة مع هذه السيناريوهات (وغيرها).


1. المفاهيم الأساسية

(1) نظرة عامة على ستة أنواع من الانضمامات

نوع الانضمام الوصف ما يُرجعه
INNER JOIN انضمام داخلي يُرجع فقط الصفوف المتطابقة في كلا الجدولين
LEFT JOIN انضمام أيسر يُرجع جميع الصفوف من الجدول الأيسر + الصفوف المتطابقة من الجدول الأيمن (NULL إذا لم يكن هناك تطابق)
RIGHT JOIN انضمام أيمن يُرجع جميع الصفوف من الجدول الأيمن + الصفوف المتطابقة من الجدول الأيسر (NULL إذا لم يكن هناك تطابق)
FULL OUTER JOIN انضمام خارجي كامل يُرجع جميع الصفوف من كلا الجدولين (NULL حيث لا يوجد تطابق)
CROSS JOIN انضمام ترافعي يُرجع الجداء الديكارتي لكلا الجدولين (جميع التركيبات)
SELF JOIN انضمام ذاتي جدول يُنضم مع نفسه

(2) فهم JOINs باستخدام مخططات فين

استخدم دائرتين A وB لتمثيل جدولين:

TEXT 📖 للعرض فقط
    ┌───────┐   ┌───────┐
    │   A   │   │   B   │
    │       │   │       │
    │   ┌───┼───┼───┐   │
    │   │ A ∩ B │   │   │
    │   └───┼───┼───┘   │
    │       │   │       │
    └───────┘   └───────┘

    INNER JOIN  → A ∩ B (تقاطع)
    LEFT JOIN   → A (جميع A + A∩B)
    RIGHT JOIN  → B (جميع B + A∩B)
    FULL JOIN   → A ∪ B (اتحاد)
    CROSS JOIN  → A × B (جميع التركيبات)

(3) INNER JOIN

يحتفظ فقط بالصفوف التي تطابقت في كلا الجدولين. تم تغطيته بالتفصيل في الدرس السابق، لذلك لن نكرره هنا.

SQL
SELECT e.name, d.name AS department
FROM employees e
INNER JOIN departments d ON e.department_id = d.id;

(4) LEFT JOIN

يُرجع جميع الصفوف من الجدول الأيسر، مع عرض أعمدة الجدول الأيمن كـ NULL حيث لا يوجد تطابق. الجدول الأيسر هو الجدول الأول بعد FROM.

SQL
SELECT e.name, d.name AS department
FROM employees e
LEFT JOIN departments d ON e.department_id = d.id;

الإخراج (لاحظ أن قسم Zhou Jiu هو NULL):

TEXT 📖 للعرض فقط
name    department
------  ----------
Zhang San Technology
Li Si   Technology
Wang Wu Marketing
Zhao Liu Finance
Qian Qi Technology
Sun Ba  Marketing
Zhou Jiu NULL
Wu Shi  Finance
💡 نصيحة: LEFT JOIN هو أحد أكثر أنواع الانضمامات استخدامًا، مناسب بشكل خاص للعثور على "سجلات بدون بيانات مرتبطة"، مثل "موظفون بدون قسم".

(5) RIGHT JOIN

يُرجع جميع الصفوف من الجدول الأيمن، مع عرض أعمدة الجدول الأيسر كـ NULL حيث لا يوجد تطابق. الجدول الأيمن هو الجدول بعد JOIN.

SQL
SELECT e.name, d.name AS department
FROM employees e
RIGHT JOIN departments d ON e.department_id = d.id;
⚠️ ملاحظة: SQLite لا يدعم RIGHT JOIN. إذا كنت تحتاج إلى تأثير RIGHT JOIN، قم بتبديل الجدولين واستخدم LEFT JOIN بدلاً من ذلك:

SQL
-- يعادل RIGHT JOIN departments d ON ...
SELECT e.name, d.name AS department
FROM departments d
LEFT JOIN employees e ON e.department_id = d.id;

(6) FULL OUTER JOIN

يُرجع جميع الصفوف من كلا الجدولين، مع عرض NULL حيث لا يوجد تطابق. يعادل اتحاد LEFT JOIN و RIGHT JOIN.

SQL
-- الصيغة (مدعومة في MySQL/PostgreSQL/SQL Server)
SELECT e.name, d.name AS department
FROM employees e
FULL OUTER JOIN departments d ON e.department_id = d.id;
⚠️ SQLite لا يدعم FULL OUTER JOIN، لكن يمكنك محاكاته باستخدام LEFT JOIN + UNION + RIGHT JOIN:

SQL
-- محاكاة FULL OUTER JOIN باستخدام LEFT JOIN UNION RIGHT JOIN
SELECT e.name, d.name AS department
FROM employees e
LEFT JOIN departments d ON e.department_id = d.id
UNION
SELECT e.name, d.name AS department
FROM employees e
RIGHT JOIN departments d ON e.department_id = d.id;

لاحظ أيضًا أن SQLite لا يدعم RIGHT JOIN. في SQLite، يمكنك تنفيذه بهذه الطريقة:

SQL
-- يعادل FULL OUTER JOIN في SQLite
SELECT e.name, d.name AS department
FROM employees e
LEFT JOIN departments d ON e.department_id = d.id
UNION
SELECT e.name, d.name AS department
FROM departments d
LEFT JOIN employees e ON e.department_id = d.id;

💡 نصيحة: في الممارسة العملية، FULL OUTER JOIN لا يُستخدم كثيرًا. معظم السيناريوهات يمكن التعامل معها باستخدام LEFT JOIN، والذي يجب أن يكون خيارك الأول.

(7) CROSS JOIN

يُرجع الداء الديكارتي لجدولين — كل تركيبة من كل صف في الجدول الأيسر مع كل صف في الجدول الأيمن. عدد صفوف النتيجة = صفوف الجدول الأيسر × صفوف الجدول الأيمن.

SQL
SELECT e.name, d.name AS department
FROM employees e
CROSS JOIN departments d;

إذا كان جدول employees به 8 صفوف وجدول departments به 4 صفوف، فستكون النتيجة 8 × 4 = 32 صفًا.

💡 نصيحة: CROSS JOIN يُستخدم عادةً لإنشاء مصفوفات تركيب (مثل "كل موظف × كل ربع" قوالب التقارير). انتبه من انفجار البيانات عند استخدامه.

(8) SELF JOIN

جدول يُنضم مع نفسه. يُستخدم عادةً لاستعلام عن "العلاقات الهرمية" أو "الأزواج داخل نفس الجدول"، مثل الموظفين ومديريهم.

SQL
-- افترض أن جدول employees يحتوي على حقل manager_id
SELECT e.name AS employee, m.name AS manager
FROM employees e
LEFT JOIN employees m ON e.manager_id = m.id;
💡 نصيحة: يجب أن يستخدم الجدول في الانضمام الذاتي أسماء مستعارة مختلفة لتمييزها (مثل e و m)، وإلا لا يمكن لـ SQL التمييز بين المرجعين.

(9) شجرة قرار اختيار الانضمام

عند مواجهة متطلبات العمل، اختر نوع الانضمام بناءً على المنطق التالي:

TEXT 📖 للعرض فقط
هل تحتاج إلى انضمام جدولين؟
├── فقط البيانات المتطابقة → INNER JOIN
├── الاحتفاظ بجميع بيانات الجدول الأيسر → LEFT JOIN
├── الاحتفاظ بجميع بيانات الجدول الأيمن → RIGHT JOIN
├── الاحتفاظ بجميع بيانات كلا الجدولين → FULL OUTER JOIN
├── تحتاج إلى جميع التركيبات → CROSS JOIN
└── الجدول يرتبط بنفسه → SELF JOIN


2. الصيغة الأساسية/الاستخدام

(1) صيغة LEFT JOIN

SQL
SELECT column_name
FROM table_A
LEFT JOIN table_B ON table_A.column = table_B.column;
💡 نصيحة: LEFT OUTER JOIN و LEFT JOIN متكافئتان تمامًا — يمكن حذف كلمة OUTER.

💡 نصيحة: في LEFT JOIN، شرط ON يؤثر فقط على مطابقة الجدول الأيمن؛ جميع صفوف الجدول الأيسر محفوظة.

(2) صيغة RIGHT JOIN

SQL
SELECT column_name
FROM table_A
RIGHT JOIN table_B ON table_A.column = table_B.column;

(3) صيغة CROSS JOIN

SQL
SELECT column_name
FROM table_A
CROSS JOIN table_B;
-- CROSS JOIN لا يتطلب شرط ON

(4) صيغة SELF JOIN

SQL
SELECT a.column, b.column
FROM table_A a
JOIN table_A b ON a.some_column = b.some_column;
💡 نصيحة: مفتاح الانضمام الذاتي هو استخدام الأسماء المستعارة "لتحويل" نفس الجدول إلى جدولين للتعامل معهما.


▶ مثال: العثور على موظفين بدون قسم (الصعوبة ⭐)

استخدم LEFT JOIN للعثور على جميع الموظفين، بما في ذلك أولئك الذين لم يُعيَّنوا لقسم:

SQL
SELECT e.name AS employee, 
       COALESCE(d.name, 'غير معيّن') AS department
FROM employees e
LEFT JOIN departments d ON e.department_id = d.id;
▶ جرّب الكود

الإخراج:

TEXT 📖 للعرض فقط
employee  department
--------  ----------
Zhang San Technology
Li Si   Technology
Wang Wu Marketing
Zhao Liu Finance
Qian Qi Technology
Sun Ba  Marketing
Zhou Jiu غير معيّن
Wu Shi  Finance

COALESCE(d.name, 'غير معيّن') يعني: إذا كان d.name هو NULL، اعرض "غير معيّن".


▶ مثال: العثور على منتجات بدون طلبات (الصعوبة ⭐⭐)

جدول products يحتوي على 8 منتجات، لكن ليس كل منتج لديه طلبات. استخدم LEFT JOIN للعثور على منتجات "صفر المبيعات":

SQL
SELECT p.name AS product, p.category, p.price,
       COUNT(o.id) AS order_count
FROM products p
LEFT JOIN orders o ON p.id = o.product_id
GROUP BY p.id, p.name, p.category, p.price
HAVING COUNT(o.id) = 0;
▶ جرّب الكود

الإخراج (يعتمد على البيانات؛ بعض المنتجات قد لا يكون لديها طلبات):

TEXT 📖 للعرض فقط
product  category  price     order_count
-------  --------  --------  -----------
Mac Mini Computer  4499.00   0
💡 النهج: أولاً استخدم LEFT JOIN لربط جميع المنتجات بالطلبات، ثم استخدم GROUP BY للتجميع، وأخيرًا استخدم HAVING لتصفية المنتجات ذات الطلبات الصفرية.


▶ مثال: الانضمام الذاتي للموظفين والمديرين (الصعوبة ⭐⭐⭐)

افترض أن جدول employees يحتوي على حقل manager_id. استعلام عن كل موظف ومديره:

SQL
-- أولاً أضف حقل manager_id والبيانات
ALTER TABLE employees ADD COLUMN manager_id INTEGER;

UPDATE employees SET manager_id = 5 WHERE id IN (1, 2, 3, 6);
UPDATE employees SET manager_id = NULL WHERE id = 5;

-- استعلام الانضمام الذاتي
SELECT e.name AS employee, 
       COALESCE(m.name, 'بلا مدير') AS manager
FROM employees e
LEFT JOIN employees m ON e.manager_id = m.id;
▶ جرّب الكود

الإخراج:

TEXT 📖 للعرض فقط
employee  manager
--------  --------
Zhang San Qian Qi
Li Si   Qian Qi
Wang Wu Qian Qi
Zhao Liu بلا مدير
Qian Qi بلا مدير
Sun Ba  Qian Qi
Zhou Jiu بلا مدير
Wu Shi  بلا مدير
💡 النهج: في الانضمام الذاتي، نفس الجدول يلعب دورين — e يمثل "النفس كموظف"، و m يمثل "النفس كمدير". العلاقة الهرمية تُنشأ عبر manager_id = m.id.



3. حالات الاستخدام الشائعة

(1) الحالة 1: إنشاء تقرير مصفوفة القسم-الموظف

استخدم CROSS JOIN لإنشاء مصفوفة كاملة "كل قسم × كل موظف":

SQL
SELECT d.name AS department, e.name AS employee
FROM departments d
CROSS JOIN employees e
WHERE e.department_id IS NOT NULL
ORDER BY d.name, e.name;

هذا مفيد لسيناريوهات مثل إنشاء "قوائم حضور لجميع الموظفين".

(2) الحالة 2: العثور على أزواج الموظفين في نفس القسم

استخدم SELF JOIN للعثور على جميع أزواج الموظفين في نفس القسم:

SQL
SELECT e1.name AS employee1, 
       e2.name AS employee2, 
       d.name AS department
FROM employees e1
INNER JOIN employees e2 ON e1.department_id = e2.department_id 
    AND e1.id < e2.id
INNER JOIN departments d ON e1.department_id = d.id;

الإخراج:

TEXT 📖 للعرض فقط
employee1  employee2  department
---------  ---------  ----------
Zhang San  Li Si      Technology
Zhang San  Qian Qi    Technology
Li Si      Qian Qi    Technology
Wang Wu    Sun Ba     Marketing
Zhao Liu   Wu Shi     Finance
💡 المفتاح: e1.id < e2.id يتجنب الأزواج المكررة (مثل "Zhang San-Li Si" و "Li Si-Zhang San" يحتفظ بواحد فقط) والأزواج الذاتية (مثل "Zhang San-Zhang San").



❓ أسئلة شائعة

س: متى تُنتج LEFT JOIN و INNER JOIN نفس النتائج؟ ج: عندما لا توجد قيم NULL في المفتاح الخارجي في الجدول الأيسر (أي جميع الصفوف تجد تطابقًا في الجدول الأيمن)، تُنتج LEFT JOIN و INNER JOIN نتائج متطابقة. ومع ذلك، LEFT JOIN يحتفظ إضافيًا بالصفوف من الجدول الأيسر التي ليس لها تطابق في الجدول الأيمن.

س: ماذا لو لم يدعم SQLite RIGHT JOIN و FULL OUTER JOIN؟ ج: يمكن تنفيذ RIGHT JOIN بتبديل ترتيب الجداول واستخدام LEFT JOIN. يمكن محاكاة FULL OUTER JOIN باستخدام LEFT JOIN + UNION + LEFT JOIN. في الممارسة العملية، RIGHT JOIN نادرًا ما يُستخدم — LEFT JOIN كافٍ في معظم الحالات.

س: هل يمكن أن يكون CROSS JOIN بطيئًا؟ ج: إذا كان كلا الجدولين يحتويان على 1000 صف، فسيُنتج CROSS JOIN مليون صف نتيجة. لذلك استخدمه بحذر شديد، تأكد أن حجم البيانات قابل للإدارة. CROSS JOIN غالبًا ما يُستخدم مع شروط WHERE، مما يجعله فعليًا يعادل INNER JOIN.

س: ما الاستخدامات العملية لـ SELF JOIN؟ ج: SELF JOIN يُستخدم عادةً للتعامل مع البيانات الهرمية (مثل الهيكل التنظيمي، ردود التعليقات، أشجار الفئات) وللعثور على "أزواج داخل نفس المجموعة" (مثل الموظفين في نفس القسم، العملاء في نفس المدينة). كلما احتجت إلى "مقارنة صف بصف داخل نفس الجدول"، قد يكون SELF JOIN مفيدًا.


📖 ملخص


📝 تمارين

التمرين 1 (⭐): اكتب استعلامًا لعرض جميع الأقسام وعدد موظفيها، بما في ذلك الأقسام بدون موظفين. مرتبة تنازليًا حسب عدد الموظفين.

التمرين 2 (⭐⭐): اكتب استعلامًا للعثور على جميع "العملاء الذين لم يقدموا أي طلب". بافتراض أننا نحتاج إلى جدول customers، يمكنك المحاكاة باستخدام حقل customer_name في جدول orders — ابحث عن العملاء الذين يظهرون فقط في جدول orders والمنتجات التي لم تُطلب قط.

التمرين 3 (⭐⭐⭐): اكتب استعلامًا باستخدام SELF JOIN للعثور على "أزواج الموظفين بتواريخ توظيف متجاوبة" — الموظفان الأقرب تواريخ توظيفهما (تلميح: استخدم دالة julianday() لحساب فروقات التواريخ).



4. الدرس التالي

👉 09-subquery - الاستعلامات الفرعية: تعلّم استخدام الاستعلامات الفرعية في WHERE وFROM وSELECT، بالإضافة إلى EXISTS/NOT EXISTS!

Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%