Menu
flag Ar iconالعربيةdown icon

LEFT JOIN في SQLite: شرح كامل مع أمثلة عملية

تعرّف على طريقة عمل LEFT JOIN في SQLite: كيف يحتفظ بكل صفوف الجدول الأيسر، ويتعامل مع NULL، ويُستخدم بأمان مع WHERE وجداول متعددة.

تحتوي هذه الصفحة على محررات قابلة للتشغيل — حرّر، شغّل، وشاهد النتيجة فوراً.

LEFT JOIN يحتفظ بكل شيء من الجدول الأيسر

عبارة INNER JOIN تُرجع فقط الصفوف التي يوجد لها تطابق في الطرفين. هذا غالبًا ما تحتاجه فعلًا، لكن ليس دائمًا. أحيانًا يكون "عدم وجود تطابق" هو بحدّ ذاته الإجابة التي تبحث عنها: المستخدمون الذين لم يُجروا أي طلب، المنتجات التي لم تُبَع قط، المقالات التي لا تحوي أي تعليق. في مثل هذه الحالات تحتاج إلى LEFT JOIN.

تقوم LEFT JOIN في SQLite بإرجاع كل صف من الجدول الأيسر. فإن وُجد صف مطابق في الجدول الأيمن، حصلت على قيم الأعمدة المطابقة. وإن لم يوجد، يبقى الصف الأيسر ظاهرًا، وتأتي أعمدة الجدول الأيمن بقيمة NULL.

كليو ليس لديها أي طلبات، ومع ذلك تظهر في النتيجة — لكن مع NULL في عمود total. لو استبدلت LEFT JOIN بـ INNER JOIN، فستختفي كليو تمامًا.

الفكرة الذهنية وراء LEFT JOIN في SQLite

اقرأ الاستعلام من الأعلى إلى الأسفل، وتخيّل أن الجدول الأول (الأيسر) هو جدول المرجع أو "المرساة". كل صف في users سيظهر في النتيجة، مهما كانت الظروف. ثم يأتي دور LEFT JOIN ليسأل عن كل مستخدم: "هل يوجد صف مطابق في جدول orders؟"

  • وُجد تطابق ← يتم لصق أعمدة الصف المطابق بصف المستخدم.
  • تطابقات متعددة ← يُنتج صفًا واحدًا لكل تطابق (لدى آدا طلبان، فتظهر مرتين).
  • لا يوجد تطابق ← يُنتج صفًا واحدًا، وتكون كل أعمدة الجدول الأيمن فيه NULL.

هذه الحالة الأخيرة هي السبب الرئيسي لوجود LEFT JOIN أصلًا. وقيمة NULL هنا لا تعني "لا نعرف"، بل تعني "لا يوجد شيء في الجدول الأيمن لإلصاقه".

أما LEFT OUTER JOIN فهو نفس العملية تمامًا. كلمة OUTER اختيارية في SQLite، ومعظم المطورين يحذفونها.

كيفية إيجاد الصفوف التي لا تملك تطابقًا

الاستخدام الكلاسيكي لـ LEFT JOIN هو إيجاد الصفوف الموجودة في الجدول الأيسر والتي ليس لها أي تطابق في الجدول الأيمن. الحيلة هنا هي أن تضع شرط التصفية على عمود من الجدول الأيمن يكون NOT NULL في البيانات الفعلية — عادةً ما يكون المفتاح الأساسي — ثم تتحقق من قيمة NULL بعد عملية الربط:

ستظهر لك «كليو» فقط. عملية الـ JOIN تربط بيانات الطلبات حيث تكون موجودة، ثم يأتي شرط WHERE o.id IS NULL ليُبقي فقط الصفوف التي فشل فيها الربط. يُطلق على هذا النمط أحيانًا اسم "anti-join" أو الربط العكسي.

الفرق بين ON و WHERE: الفخّ الخفي

هذا أكثر خطأ شائع يقع فيه المبرمجون عند استخدام LEFT JOIN، ويستحق وقفة متأنية. الشروط يمكن وضعها إما داخل جملة ON أو داخل جملة WHERE، لكن سلوكها مع الـ outer joins يختلف اختلافًا جوهريًا.

  • جملة ON تُنفَّذ أثناء عملية الربط نفسها، وشروطها تُحدّد أيّ صفوف من الجدول الثاني تُعتبر مطابقة.
  • جملة WHERE تُنفَّذ بعد أن ينتهي الربط من إنتاج صفوفه، فهي تُصفّي النتيجة النهائية المدمجة.

والآن لاحظ ما يحدث عندما تضع شرطًا على الجدول الثاني داخل WHERE:

كليو ليس لديها أي طلب، لذلك تكون قيمة o.status بالنسبة لصفّها هي NULL، والمقارنة NULL = 'shipped' لا تُعتبر صحيحة، فيتم استبعادها من النتائج. وبوريس حالته 'pending' فيُستبعد هو الآخر. وهكذا يكون LEFT JOIN قد تحوّل ضمنيًا إلى INNER JOIN دون أن تشعر.

الحل: ننقل الشرط إلى داخل ON، حتى يعمل على تصفية التطابقات لا الصفوف الناتجة:

الآن يظهر كل المستخدمين في النتيجة. آدا يظهر معها طلبها المُرسَل، وبوريس تظهر معه قيمة NULL (لأن طلبه المعلّق لم يحقق شرط المطابقة)، وكليو أيضًا تظهر معها NULL (لأنها لا تملك أي طلبات أصلًا). وهذه هي الإجابة الصحيحة حين يكون السؤال: "اعرض لي كل المستخدمين، مع طلباتهم المُرسَلة إن وُجدت".

قاعدة عامة تستحق الحفظ: الشروط المتعلقة بالجدول الأساسي (يسار العملية) يمكن وضعها في WHERE، أما الشروط المتعلقة بالجدول الثانوي فمكانها الطبيعي داخل ON — إلا إذا كنت تقصد تحديدًا اصطياد الصفوف غير المتطابقة عبر IS NULL.

العدّ باستخدام LEFT JOIN في SQLite

من أكثر المهام شيوعًا: عدّ الصفوف المرتبطة بكل سجل أب، مع تضمين الآباء الذين ليس لديهم أي صفوف مرتبطة. لو استخدمت INNER JOIN فستختفي الأصفار من النتيجة. لكن مع LEFT JOIN ودالة COUNT على عمود من الجدول الثانوي تحصل على الإجابة الصحيحة:

شيئان يستحقان الانتباه:

  • COUNT(o.id) يعدّ الصفوف غير الفارغة من الجهة الأخرى للربط. لذلك تحصل Cleo على 0 وليس 1، لأن COUNT يتجاهل قيم NULL. أما لو كتبت COUNT(*) فستحصل Cleo على 1 (الصف موجود فعلاً، لكن قيمه NULL). في الغالب، COUNT(right.id) هو ما تريده فعلاً.
  • COALESCE(SUM(o.total), 0) يحوّل ناتج جمع Cleo من NULL إلى 0. بدونه ستظهر إيراداتها كـ NULL، وهو أمر صحيح تقنياً لكنه قبيح المنظر عند العرض.

ربط جداول متعددة في SQLite

يمكن تسلسل عدة LEFT JOIN خلف بعضها. كل عملية ربط تأخذ النتيجة المتراكمة وتضم إليها جدولاً جديداً. وبمجرد أن تجعل عموداً قابلاً لاحتواء NULL عبر LEFT JOIN، استمر باستخدام LEFT JOIN مع أي جدول يتفرع منه — وإلا فإن أول INNER JOIN تالٍ سيُسقط بصمت الصفوف التي كنت تريد الاحتفاظ بها.

تعود إلينا ثلاثة مستخدمين. آدا لديها طلب وشحنة. بوريس لديه طلب لكن بدون شحنة (قيمة carrier تساوي NULL). أمّا كليو فليس لديها أي طلب، ولذلك تظهر كلٌّ من o.total وs.carrier بقيمة NULL. سلسلة LEFT JOIN تحافظ على كل مستخدم بصرف النظر عن الموضع الذي تنقطع عنده البيانات في سلسلة العلاقات.

متى يكون LEFT JOIN هو الخيار الصحيح

استخدم LEFT JOIN عندما يكون السؤال في جوهره عن الجدول الأيسر، ويكون الجدول الآخر مجرد معلومات إضافية. صياغات مثل «كل المستخدمين مع طلباتهم إن وُجدت» أو «جميع المنتجات مع آخر تقييم لها» تُترجَم مباشرةً إلى LEFT JOIN.

أمّا INNER JOIN فهو الخيار حين يكون الطرفان مطلوبَين بالقدر نفسه — فعبارة «الطلبات مع بيانات أصحابها» لا معنى لها إذا كان هناك طلب بلا مستخدم، وهنا يكون الفلترة التي يقوم بها الـ inner join هي ما تحتاجه فعلاً.

إذا وجدت نفسك تكتب LEFT JOIN ... WHERE right.col IS NOT NULL، فأنت في الحقيقة كنت تريد INNER JOIN. وإذا وجدت نفسك تكتب LEFT JOIN ... WHERE right.col IS NULL، فأنت تريد anti-join، وقد كتبتَه بشكل صحيح.

التالي: الربط الذاتي (Self-Joins)

أحياناً يكون الجدول الذي تريد الربط معه هو نفس الجدول الذي تستعلم منه أصلاً — الموظفون ومدراؤهم، الأقسام والأقسام الأم، أزواج المستخدمين في المدينة نفسها. هذا ما يُعرف بالربط الذاتي (self-join)، وهو موضوع الصفحة التالية.

الأسئلة الشائعة

ماذا يفعل LEFT JOIN في SQLite؟

LEFT JOIN يُرجع كل صفوف الجدول الأيسر، ومعها الصفوف المطابقة من الجدول الأيمن إن وُجدت. أما إذا لم يكن هناك تطابق في الجدول الأيمن، فستظل تحصل على صف الجدول الأيسر، وتظهر أعمدة الجدول الأيمن بقيمة NULL. وبالمناسبة، LEFT OUTER JOIN هو نفسه LEFT JOIN تمامًا — كلمة OUTER اختيارية في SQLite.

ما الفرق بين LEFT JOIN و INNER JOIN في SQLite؟

INNER JOIN يُرجع فقط الصفوف التي يتحقق فيها شرط الربط في كلا الجدولين. أما LEFT JOIN فيُرجع جميع صفوف الجدول الأيسر بصرف النظر عن وجود تطابق، ويملأ أعمدة الجدول الأيمن غير المطابقة بقيمة NULL. استخدم LEFT JOIN عندما يكون 'عدم وجود تطابق' معلومة مهمة بحد ذاتها — مثلًا إيجاد المستخدمين الذين لا يملكون أي طلبات.

لماذا يتصرف LEFT JOIN في SQLite كأنه INNER JOIN؟

السبب في الغالب هو وجود شرط WHERE يقوم بالتصفية على عمود من الجدول الأيمن دون مراعاة قيم NULL. الشروط المتعلقة بالجدول الأيمن يجب أن تُكتب داخل ON، لا داخل WHERE، أو أن تكتب WHERE right.col IS NULL للعثور على الصفوف غير المطابقة. لأن كتابة WHERE right.col = 'x' تُسقط بصمت كل الصفوف غير المطابقة وتُحوّل النتيجة فعليًا إلى INNER JOIN.

Coddy programming languages illustration

تعلّم البرمجة مع Coddy

ابدأ الآن