EXISTS وNOT EXISTS المترابطان
البديل المتين لـ Anti-Join الذي يتعامل مع NULL بشكل صحيح
EXISTS وNOT EXISTS المترابطان درس مجاني في SQL Interview Prep على CoddyKit. هذا هو الدرس 3 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في SQL Interview Prep، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة SQL Interview Prep 4 دروس في المجموع.
تختبر EXISTS وجود الصفوف
تأخذ EXISTS استعلامًا فرعيًا وتعيد TRUE بمجرد أن يعيد ذلك الاستعلام صفًا واحدًا على الأقل، وإلا فتعيد FALSE. وهي لا تعيد الصفوف نفسها مطلقًا.
عند استخدام استعلام فرعي مترابط داخلها، تصبح EXISTS اختبارًا لوجود صف لكل صف خارجي: "هل يوجد صف مطابق لهذا الصف الخارجي؟"
ولأنها تتوقف عند أول تطابق، فهي لا تهتم بعدد الصفوف المطابقة. وهذه التفصيلة الدلالية من النقاط المفضلة في المقابلات.
EXISTS مترابطة أساسية
اعثر على العملاء الذين أجروا طلبًا واحدًا على الأقل. يرتبط الاستعلام الداخلي من خلال o.customer_id = c.customer_id.
بالنسبة إلى كل عميل، تسأل EXISTS: هل يوجد أي طلب لهذا العميل؟ إذا كانت الإجابة نعم، فاحتفظ بالعميل.
SELECT c.customer_id, c.name
FROM customers c
WHERE EXISTS (
SELECT 1
FROM orders o
WHERE o.customer_id = c.customer_id
);لماذا نستخدم SELECT 1 داخل EXISTS
سترى SELECT 1 أو SELECT * أو SELECT NULL داخل EXISTS. وجميعها متكافئة.
تتحقق EXISTS فقط مما إذا كانت الصفوف ستُعاد، ولا تهتم بمحتواها مطلقًا، لذلك تكون الأعمدة الناتجة غير مهمة. ويتجاهلها المحسّن.
يُعد SELECT 1 اصطلاحًا شائعًا يوضح القصد: "أنا أهتم بالوجود فقط." اختر صيغة واحدة والتزم بها؛ ولا تدع القائم بالمقابلة يظن أن قائمة الأعمدة مهمة هنا.
تعثر NOT EXISTS على المفقود
تعكس NOT EXISTS الاختبار: لا تحتفظ بالصف الخارجي إلا عندما لا يعيد الاستعلام الفرعي المترابط أي صفوف.
وهذا هو anti-join التقليدي: العملاء الذين لا طلبات لهم، والمنتجات التي لم تُبع قط، والطلاب الذين لا تسليمات لهم.
SELECT c.customer_id, c.name
FROM customers c
WHERE NOT EXISTS (
SELECT 1
FROM orders o
WHERE o.customer_id = c.customer_id
);فخ NULL مع NOT IN
إليك نقطة مهمة في المقابلات. يتصرف NOT IN بشكل خطير عند استخدامه مع استعلام فرعي يمكن أن يحتوي على NULL: فإذا احتوت القائمة على NULL واحد فقط، أعاد NOT IN لا صفوف على الإطلاق.
يحدث ذلك لأن المقارنة مع NULL تنتج UNKNOWN، ولأن NOT IN يتطلب أن تكون كل المقارنات خاطئة. فتؤدي قيمة UNKNOWN واحدة إلى إفساد الشرط بأكمله.
ولا تعاني NOT EXISTS من هذه المشكلة؛ فهي تتحقق ببساطة من وجود الصفوف وتتعامل مع NULL بأمان.
-- Risky: returns nothing if any o.customer_id is NULL
SELECT c.customer_id FROM customers c
WHERE c.customer_id NOT IN (SELECT o.customer_id FROM orders o);
-- Safe: NULLs do not break it
SELECT c.customer_id FROM customers c
WHERE NOT EXISTS (
SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id
);لماذا تكون NOT EXISTS آمنة مع NULL
يكمن السبب في منطق المطابقة. تتحقق NOT EXISTS مما إذا كان أي صف داخلي يحقق الشرط o.customer_id = c.customer_id.
لا يحقق الصف الذي تكون فيه قيمة o.customer_id مساوية لـ NULL هذه المساواة مطلقًا، لأن NULL = أي شيء ينتج UNKNOWN وليس TRUE؛ ولذلك لا يُحتسب ببساطة كتطابق. ويبقى اختبار الوجود صحيحًا.
أما مع NOT IN، فتصبح قيمة NULL نفسها جزءًا من مقارنة قائمة، وتؤدي نتيجة UNKNOWN إلى محو جميع النتائج. ولهذا يفضّل أصحاب الخبرة في المقابلات التقنية استخدام NOT EXISTS.
EXISTS مع شروط إضافية
يمكن للاستعلام الفرعي المترابط أن يتضمن شروطًا إضافية. ابحث عن العملاء الذين قدّموا طلبًا واحدًا على الأقل يتجاوز 1000.
يوجد الشرط الإضافي داخل الاستعلام الفرعي لـ EXISTS، ويُطبَّق ضمن نطاق كل عميل.
SELECT c.customer_id, c.name
FROM customers c
WHERE EXISTS (
SELECT 1
FROM orders o
WHERE o.customer_id = c.customer_id
AND o.amount > 1000
);الأداء: سلوك التوقف المبكر
يمكن لـ EXISTS التوقف عن فحص العلاقة الداخلية بمجرد العثور على صف مطابق. فهو لا ينشئ مجموعة نتائج كاملة ولا يحسب عدد عناصرها.
وهذا يجعل EXISTS فعّالًا عادةً، خصوصًا عندما يكون العمود المترابط مفهرسًا، لأن كل عملية بحث لكل صف يمكنها العثور على تطابق بسرعة ثم التوقف.
قارن ذلك بـ COUNT(*) > 0 المترابط، الذي يفرض عدّ كل التطابقات. استخدم EXISTS عندما تحتاج فقط إلى إجابة بنعم أو لا.
EXISTS مقابل COUNT للتحقق من الوجود
قد يكتب بعض المرشحين عددًا مترابطًا لاختبار وجود البيانات. يعمل ذلك، لكنه يهدر الجهد.
يحصي إصدار COUNT كل طلب مطابق، بينما يتوقف EXISTS بعد الأول. وفي اختبار وجود بحت، يعبّر EXISTS عن المقصود بوضوح ويتيح للمحسّن إيقاف التنفيذ مبكرًا.
-- Works but counts everything
SELECT c.customer_id FROM customers c
WHERE (SELECT COUNT(*) FROM orders o
WHERE o.customer_id = c.customer_id) > 0;
-- Better: stops at first match
SELECT c.customer_id FROM customers c
WHERE EXISTS (SELECT 1 FROM orders o
WHERE o.customer_id = c.customer_id);مثال تطبيقي: المنتجات التي لم تُطلب مطلقًا
هذا سؤال كلاسيكي في المقابلات عن الربط المضاد (anti-join): اعرض المنتجات التي لم يطلبها أحد قط. وتُقرأ NOT EXISTS تقريبًا كالمتطلب نفسه.
لكل منتج، تحقّق مما إذا كان أي سطر طلب يشير إليه؛ واحتفظ فقط بالمنتجات التي لا يوجد لها أي سطر.
SELECT p.product_id, p.name
FROM products p
WHERE NOT EXISTS (
SELECT 1
FROM order_items oi
WHERE oi.product_id = p.product_id
);استخدام EXISTS داخل NOT EXISTS في استعلامات القسمة
يعبّر تداخل EXISTS داخل NOT EXISTS عن القسمة العلائقية: «العثور على الصفوف التي تطابق مجموعةً بأكملها.» ومن الأسئلة الكلاسيكية: «العملاء الذين طلبوا كل منتج في فئة.»
المنطق هو: احتفظ بالعميل عندما لا يوجد منتج لم يطلبه. وهذا النفي المزدوج هو السمة المميزة لاستعلام القسمة، ويستخدمه المحاورون لاختبار إلمامك العميق بـ EXISTS.
SELECT c.customer_id
FROM customers c
WHERE NOT EXISTS (
SELECT 1 FROM products p
WHERE p.category = 'Coffee'
AND NOT EXISTS (
SELECT 1 FROM order_items oi
JOIN orders o ON o.order_id = oi.order_id
WHERE oi.product_id = p.product_id
AND o.customer_id = c.customer_id
)
);تحقق سريع
اختر الطريقة الأكثر أمانًا للعثور على العملاء الذين ليس لديهم طلبات.
مراجعة: EXISTS وNOT EXISTS المترابطان
أهم النقاط:
EXISTSهو اختبار وجود يُجرى لكل صف، ويتوقف عند أول تطابق؛ ولا يؤثر اختيار العمود بداخله (استخدمSELECT 1).NOT EXISTSهو الربط المضاد الآمن تجاه NULL للعثور على الصفوف التي لا تطابق شيئًا.- تُعيد
NOT INمع وجود NULL في القائمة لا شيء؛ لذا فضّلNOT EXISTS. - في اختبار الوجود، يتفوق EXISTS على
COUNT(*) > 0المترابط لأنه يتوقف مبكرًا.
اذكر تلقائيًا مشكلة NULL مع NOT IN؛ فهي مؤشر موثوق على نضجك في SQL.
الأسئلة الشائعة
هل درس «EXISTS وNOT EXISTS المترابطان» مجاني؟
نعم — نص درس «EXISTS وNOT EXISTS المترابطان» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة SQL Interview Prep، انتقل إلى CoddyKit PRO. تتضمن دورة SQL Interview Prep 4 دروس في المجموع.
ماذا ستتعلم في «EXISTS وNOT EXISTS المترابطان»؟
البديل المتين لـ Anti-Join الذي يتعامل مع NULL بشكل صحيح تتمرن على SQL Interview Prep مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ SQL Interview Prep؟
لا تُشترط خبرة سابقة. SQL Interview Prep على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 4.
كم من الوقت يستغرق درس «EXISTS وNOT EXISTS المترابطان»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس SQL Interview Prep هذا؟
نعم. كل درس في SQL Interview Prep يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- تشريح استعلام فرعي مترابط
- تجميعات كل مجموعة دون GROUP BY
- EXISTS وNOT EXISTS المترابطان
- إعادة كتابة الاستعلامات الفرعية المترابطة كعمليات ربط