مقدمة في استعلامات JOIN

مقدمة في استعلامات JOIN

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


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

لماذا نحتاج إلى JOINs؟

عند تصميم قاعدة بيانات، نقسم البيانات إلى جداول متعددة لتجنب التكرار. على سبيل المثال، جدول employees يخزّن فقط department_id، بدلاً من تكرار اسم القسم وعنوانه. لكن الاستعلامات غالبًا ما تحتاج إلى "تجميع" المعلومات الكاملة مرة أخرى، وهنا تأتي JOINs.

SQL
-- بدون JOIN: لا يمكن رؤية department_id، فقط اسم القسم
SELECT name, department_id, salary FROM employees;

-- مع JOIN: اسم الموظف واسم القسم يظهران معًا
SELECT e.name, d.name AS department, e.salary
FROM employees e
JOIN departments d ON e.department_id = d.id;

أنواع العلاقات بين الجداول

نوع العلاقة الوصف مثال
واحد لواحد (1:1) صف واحد في الجدول A يقابل صفًا واحدًا في الجدول B مستخدم واحد لديه بطاقة هوية واحدة
واحد لعديد (1:N) صف واحد في الجدول A يقابل عدة صفوف في الجدول B قسم واحد لديه عدة موظفين
عديد لعديد (M:N) عدة صفوف في الجدول A تقابل عدة صفوف في الجدول B الطلاب والمقررات (مرتبطة عبر جدول التسجيل)

في قاعدة البيانات الخاصة بنا:

المفتاح الخارجي

المفتاح الخارجي هو الجسر الذي يُنشئ العلاقات بين الجداول. وهو عمود في الجدول A يشير إلى المفتاح الأساسي في الجدول B، مما يضمن اتساق البيانات.

SQL
-- department_id في جدول employees هو مفتاح خارجي يشير إلى id في جدول departments
CREATE TABLE employees (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    department_id INTEGER,           -- مفتاح خارجي
    salary DECIMAL(10,2),
    hire_date DATE,
    FOREIGN KEY (department_id) REFERENCES departments(id)
);
💡 نصيحة: المفاتيح الخارجية تفرض "سلامة المراجع" — لا يمكنك تعيين معرف قسم غير موجود لموظف. ومع ذلك، في SQLite، المفاتيح الخارجية معطلة بشكل افتراضي.تحتاج إلى تنفيذ PRAGMA foreign_keys = ON; لتفعيلها.

صيغة INNER JOIN

INNER JOIN يُرجع فقط الصفوف التي تطابقت في كلا الجدولين. إذا لم يكن لدى صف بيانات مقابلة في الجدول الآخر، يتم استبعاده.

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

شرط ON

يُحدد شرط ON شرط الانضمام — أي العمود المستخدم لمطابقة الصفوف بين الجدولين. عادةً يقارن مفتاحًا خارجيًا بمفتاح أساسي.

SQL
-- شرط ON: employees.department_id = departments.id
SELECT e.name, d.name AS department
FROM employees e
INNER JOIN departments d ON e.department_id = d.id;

أسماء الجداول المستعارة (AS)

عندما تكون أسماء الجداول طويلة أو تحتاج إلى التمييز بين أعمدة لها نفس الاسم، يمكنك استخدام AS لإنشاء أسماء مستعارة للجداول، مما يُبسّط الكتابة:

SQL
-- بدون أسماء مستعارة: يجب كتابة الأسماء الكاملة في كل مرة
SELECT employees.name, departments.name
FROM employees
INNER JOIN departments ON employees.department_id = departments.id;

-- مع أسماء مستعارة: أكثر إيجازًا
SELECT e.name, d.name
FROM employees e
INNER JOIN departments d ON e.department_id = d.id;
💡 نصيحة: بمجرد تحديد اسم مستعار للجدول، يجب استخدام الاسم المستعار في جميع أنحاء الاستعلام — لا يمكنك استخدام اسم الجدول الأصلي مرة أخرى.


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

صيغة INNER JOIN الكاملة

SQL
SELECT table_A.column1, table_A.column2, table_B.column1
FROM table_A
INNER JOIN table_B ON table_A.foreign_key = table_B.primary_key
WHERE condition
ORDER BY column_name;
💡 نصيحة: يمكن حذف كلمة INNER — كتابة JOIN وحدها تعادل INNER JOIN.

💡 نصيحة: عندما يكون اسم العمود فريدًا في كلا الجدولين، يمكنك تخطي بادئة الجدول. ومع ذلك، من الجيد دائمًا استخدام البادئات لتجنب الغموض.

💡 نصيحة: يمكن لانضمام واحد ربط عدة جداول — فقط قم بتسلسلها: A JOIN B ON ... JOIN C ON ....


مثال: استعلام عن الموظفين وأقسامهم (الصعوبة ⭐)

الأساسية — عرض اسم القسم الذي ينتمي إليه كل موظف:

SQL
SELECT e.name AS employee, d.name AS department, e.salary
FROM employees e
INNER JOIN departments d ON e.department_id = d.id;
▶ جرّب الكود

الإخراج:

TEXT
employee  department  salary
--------  ----------  --------
Zhang San Technology  15000.00
Li Si     Technology  18000.00
Wang Wu   Marketing   12000.00
Zhao Liu  Finance     13000.00
Qian Qi   Technology  20000.00
Sun Ba    Marketing   11000.00
Wu Shi    Finance     14000.00

ملاحظة: Zhou Jiu لا يظهر لأن department_id الخاص به هو NULL. INNER JOIN يحتفظ فقط بالصفوف التي تتطابق في كلا الجدولين.


مثال: استعلام عن تفاصيل الطلب (انضمام ثلاثة جداول) (الصعوبة ⭐⭐)

في سيناريوهات الأعمال الحقيقية، انضمام عدة جداول أمر شائع. استعلام عن اسم العميل واسم المنتج والسعر الفردي والإجمالي لكل طلب:

SQL
SELECT o.customer_name, p.name AS product,
       p.price, o.quantity,
       (p.price * o.quantity) AS total
FROM orders o
INNER JOIN products p ON o.product_id = p.id
ORDER BY total DESC;
▶ جرّب الكود

الإخراج:

TEXT
customer_name  product         price     quantity  total
-------------  --------------  --------  --------  --------
Xiao Li        iPad Air        4799.00   3         14397.00
Xiao Gang      MacBook Pro     12999.00  1         12999.00
Xiao Wang      iPhone 15       5999.00   2         11998.00
Xiao Ming      iPhone 15       5999.00   1         5999.00
Xiao Gang      iPhone 14       4999.00   1         4999.00
Xiao Hong      Magic Keyboard  999.00    5         4995.00
Xiao Ming      Apple Watch     2999.00   1         2999.00
Xiao Hong      AirPods Pro     1899.00   2         3798.00

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

الحالة 1: استعلام عن جميع موظفي قسم التكنولوجيا

أولاً ابحث عن معرف القسم عبر JOIN، ثم قم بالتصفية باستخدام WHERE:

SQL
SELECT e.name, e.salary, e.hire_date
FROM employees e
INNER JOIN departments d ON e.department_id = d.id
WHERE d.name = 'Technology';

الإخراج:

TEXT
name    salary    hire_date
------  --------  ----------
Zhang San 15000.00  2023-01-15
Li Si   18000.00  2022-06-01
Qian Qi 20000.00  2020-08-05

الحالة 2: عدد الموظفين في كل قسم

SQL
SELECT d.name AS department, COUNT(e.id) AS emp_count
FROM departments d
INNER JOIN employees e ON d.id = e.department_id
GROUP BY d.name;

الإخراج:

TEXT
department  emp_count
----------  ---------
Technology  3
Marketing   2
Finance     2

❓ أسئلة شائعة

س: أيهما أفضل أداءً، JOIN أم الاستعلام الفرعي؟ ج: بشكل عام، JOINs أفضل أداءً لأن محركات قواعد البيانات تحتوي على محسّنات متخصصة لـ JOINs. الاستعلامات الفرعية في بعض الحالات قد تُنتج جداول مؤقتة، مما يؤدي إلى أداء أسوأ قليلاً. ومع ذلك، يعتمد ذلك على حجم البيانات والفهرسة — استخدم EXPLAIN لفحص خطة التنفيذ.

س: ما الفرق بين ON و WHERE؟ ج: ON هو شرط الانضمام الذي يُحدد كيفية إقران الجدولين؛ WHERE هو شرط التصفية الذي يُحدد أي الصفوف يتم عرضها. لهما تأثيرات متشابهة في INNER JOIN، لكنهما يختلفان بشكل كبير في LEFT JOIN — ON لا يؤثر على الاحتفاظ بالجدول الأيسر، بينما WHERE يُصفّر الصفوف الفارغة.

س: هل أسماء الجداول المستعارة مطلوبة؟ ج: ليست مطلوبة، لكن يُوصى بها بشدة. أسماء الجداول المستعارة تجعل SQL أكثر إيجازًا، خاصة في الانضمامات متعددة الجداول حيث تُحسّن القراءة بشكل كبير.

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


📖 ملخص


📝 تمارين

التمرين 1 (⭐): اكتب استعلامًا لعرض اسم العميل واسم المنتج وفئة المنتج لجميع الطلبات (يتطلب انضمام جدولي orders و products).

التمرين 2 (⭐⭐): اكتب استعلامًا لعرض متوسط الراتب لكل قسم، عرض فقط الأقسام التي يتجاوز متوسط رواتبها 12000 (تلميح: JOIN + GROUP BY + HAVING).

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


الدرس التالي

👉 08-join-types - أنواع الانضمامات موضحة: تعمّق في LEFT JOIN وRIGHT JOIN وFULL OUTER JOIN وCROSS JOIN وSELF JOIN، وأتقن استراتيجية اختيار نوع الانضمام المناسب!

Web-Tutorial.com

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

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

100%