ربط الجداول في SQLite باستخدام INNER JOIN
قواعد البيانات العلاقية تُقسّم البيانات على عدة جداول عن قصد: العملاء في جدول، الطلبات في جدول آخر، والمنتجات في جدول ثالث. الهدف من هذا التقسيم هو أن تبقى كل معلومة في مكان واحد فقط. لكن عندما تريد الإجابة على سؤال حقيقي مثل: "أي عميل طلب أي منتج؟"، ستحتاج إلى تجميع هذه القطع من جديد، وهنا يأتي دور عملية الربط (Join).
ويُعدّ INNER JOIN هو الأداة الأكثر استخدامًا في هذا السياق؛ فهو يدمج الصفوف من جدولين كلما تحقّق الشرط الذي تُحدّده، ويتجاهل أي صفوف لا تتطابق.
ثلاثة عملاء، وثلاثة طلبات، لكن «شين» ليس لديه أي طلب — لذلك لن يظهر في النتيجة. هذا هو جوهر كلمة "inner": لا تبقى إلا الصفوف المتطابقة فقط.
الفكرة الذهنية: طابِق الصفوف ثم صفِّ النتائج
اقرأ جملة INNER JOIN بهذه الطريقة: خذ كل صف من الجدول الأول، وقارنه بكل صف في الجدول الثاني، ثم احتفظ بالزوج فقط عندما يتحقق شرط ON. من الناحية المفاهيمية، الأمر أشبه بحاصل ضرب ديكارتي ضخم يتبعه عملية تصفية. طبعًا SQLite لا ينفّذ الاستعلام فعليًا بهذه الطريقة (فهو يستخدم الفهارس متى أمكن)، لكن هذا النموذج الذهني دقيق بما يكفي لتتوقع شكل النتيجة الخارجة.
بعض العادات التي يستحق إنك تتبناها هنا:
- استخدم اسم مستعار للجداول (
customers AS c) لما تحتاج تذكرها أكثر من مرة. يخفّف من الفوضى. - وضّح اسم العمود مع الجدول (
c.name,o.total) لما يكون العمود ممكن يكون من أي جدول من الاثنين. - ترتيب الشرط في
ON o.customer_id = c.idما يفرق — تكتبهc.id = o.customer_idويشتغل بنفس الطريقة.
الفرق بين INNER JOIN و JOIN
في SQLite (وفي SQL القياسي عمومًا)، لما تكتب JOIN لحالها فهي تعني INNER JOIN. كلمة INNER اختيارية وممكن تستغني عنها.
كلا الأسلوبين يُنتجان نفس خطة التنفيذ ونفس الصفوف. لكن كتابة INNER JOIN بشكل صريح تُحسّن من قراءة الكود قليلاً، خصوصاً عندما تختلط أنواع الـ joins داخل نفس الاستعلام — فتظهر النية واضحة بجانب LEFT JOIN المكتوب بعدها بأسطر قليلة.
الفرق بين ON و USING في sqlite
عندما يحمل عمود الربط نفس الاسم في الجدولين، تكون USING (column) أقصر من ON a.col = b.col:
USING (customer_id) يقوم بأمرين معًا: يطابق الصفوف عند تساوي قيمة customer_id، ويدمج العمود ليظهر مرة واحدة فقط في النتيجة. استخدمه عندما يحمل الجدولان فعلًا نفس اسم العمود. أما إذا اختلفت الأسماء (orders.customer_id = customers.id) أو كان الشرط أكثر من مجرد مساواة بسيطة، فالأفضل البقاء مع ON.
ربط ثلاث جداول في sqlite باستخدام inner join
لربط أكثر من جدولين، أضف عبارات JOIN ... ON ... متتالية. كل عبارة تربط النتيجة المتراكمة بجدول جديد.
اقرأها من الأعلى إلى الأسفل: العملاء يرتبطون بالطلبات، والطلبات ترتبط بالعناصر. كل صف في النتيجة يمثّل تركيبة واحدة من عميل وطلب وعنصر. أي صف لا يجد مطابقاً له في أي حلقة من هذه السلسلة سيُستبعد — وهذه هي قاعدة INNER JOIN المطبَّقة في كل خطوة.
التصفية باستخدام WHERE
ON تحدّد كيفية مزاوجة الصفوف، بينما WHERE تُصفّي النتيجة بعد المزاوجة. تحديداً في حالة INNER JOIN، وضع شرط إضافي داخل ON أو داخل WHERE سيُنتج نفس الصفوف — لكن العُرف المتّبع هو إبقاء شروط الربط في ON وشروط تصفية الصفوف في WHERE.
هذا يُقرأ كالتالي: "اربط جدولي العملاء والطلبات، ثم احتفظ فقط بالعملاء البريطانيين الذين تجاوز مبلغ طلبهم 20." دوران، وجملتان شرطيتان — وستشكر نفسك لاحقاً على هذا الفصل. (حين تبدأ بكتابة LEFT JOIN، سيتضح أن الفرق بين ON وWHERE ليس شكلياً أبداً، لكن هذا موضوع الصفحة التالية.)
شروط متعددة داخل ON
عبارة ON تقبل أي تعبير منطقي، وليس مجرد مساواة واحدة. هذا مفيد عندما تكون العلاقة بين الجدولين ممتدة على أكثر من عمود، أو حين تريد تصفية الجدول التابع أثناء عملية الربط نفسها.
يختفي الطلب الملغي لأنّ الشرط الثاني لم يتحقق. وفي حالة inner join تحديدًا، يمكنك كتابة WHERE o.status = 'paid' وستحصل على النتيجة نفسها. لكن استخدام ON يُبقي منطق "ما الذي يُعتبر تطابقًا" ملتصقًا بعملية الربط نفسها.
أخطاء شائعة عند استخدام inner join
هذه بعض النقاط التي يقع فيها كثيرون:
- نسيان جملة
ON. كتابةFROM a INNER JOIN bبدونONيُعدّ خطأً نحويًا في SQLite. (أمّا الفاصلة المجردة —FROM a, b— فتُنفَّذ بالفعل، لكنها تُنتج cross join، وهو غالبًا ليس ما تريده.) - صفوف مكررة لم تتوقعها. إذا كان لدى عميلٍ ما ثلاثة طلبات، فسيظهر اسمه ثلاث مرات في النتيجة. هذا سلوك طبيعي للـ join وليس خللًا. استخدم
GROUP BYإذا أردت صفًا واحدًا لكل عميل. - صفوف مفقودة. إذا كان من المفترض أن يظهر عميل ولم يظهر، فهذا يعني أنّ شرط الربط لم يتحقق — تحقّق من وجود قيم
NULLفي أعمدة الربط، أو لجأ إلىLEFT JOIN. - أسماء أعمدة ملتبسة. الاستعلام
SELECT id FROM customers JOIN orders ON ...يُسبّب خطأً لأنّ الجدولين يحتويان على عمودid. عليك تحديد المصدر:c.idأوo.id.
التالي: LEFT JOIN
يُعدّ INNER JOIN خيارًا ممتازًا حين يعني غياب التطابق "تجاهل هذا الصف". لكن في بعض الأحيان تريد إظهار كل العملاء، حتى أولئك الذين ليس لديهم أي طلبات، مع ظهور قيم NULL مكان البيانات المفقودة. هنا يأتي دور LEFT JOIN، وهو موضوع الدرس التالي.
الأسئلة الشائعة
ما الذي يفعله INNER JOIN في SQLite؟
ببساطة، INNER JOIN يُرجع الصفوف التي لها تطابق في الجدولين معًا حسب الشرط المكتوب في ON. أي صف لا يجد ما يقابله في الطرف الآخر يُستبعد من النتيجة. وهو الافتراضي في SQLite، لذا فإن JOIN و INNER JOIN يعنيان نفس الشيء تمامًا.
ما الفرق بين INNER JOIN و LEFT JOIN في SQLite؟
الـ INNER JOIN يحتفظ فقط بالصفوف المتطابقة، أما LEFT JOIN فيُبقي كل صفوف الجدول الأيسر ويضع NULL في أعمدة الجدول الأيمن عند غياب التطابق. استخدم INNER JOIN عندما يعني عدم وجود تطابق «تجاهل هذا الصف»، واستخدم LEFT JOIN عندما تريد أن يظهر الصف على أي حال.
هل يمكن عمل INNER JOIN لثلاثة جداول في SQLite؟
نعم، فقط أضف جملة JOIN ... ON ... أخرى. كل عملية ربط تصل النتيجة الحالية بجدول جديد. لا يوجد حد صارم للعدد، لكن تذكّر أن قراءة الاستعلام تصبح متعبة بعد أربعة أو خمسة جداول، وهنا تكون الـ CTE خيارًا أنظف بكثير.
متى أستخدم USING بدل ON؟
USING (column) اختصار مفيد عندما يحمل عمود الربط نفس الاسم في الجدولين. صياغته أقصر، كما أنه يدمج العمود المكرر في عمود واحد بالنتيجة. أما ON فاستخدمه إذا اختلفت أسماء الأعمدة أو كنت تحتاج شرطًا أكثر تعقيدًا.