0Pricing
Coding Interview Prep · درس

إعادة كتابة الاستعلامات الفرعية المترابطة كعمليات ربط

تحويل المنطق المترابط إلى عمليات ربط أو دوال نافذة لتحسين الأداء

إعادة كتابة الاستعلامات الفرعية المترابطة كعمليات ربط درس مجاني في Coding Interview Prep على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Coding Interview Prep، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Coding Interview Prep 4 دروس في المجموع.

لماذا نعيد الكتابة أصلًا

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

الهدف هو الحصول على النتيجة نفسها بمرور واحد على البيانات بدلًا من عمليات الفحص الداخلية المتكررة.

تُعد معرفة نمطين أو ثلاثة من أنماط إعادة الكتابة، ومعرفة متى يحافظ كل منها على صحة النتيجة، مهارة أساسية في المستوى المتوسط.

النمط 1: تحويل EXISTS إلى INNER JOIN

يمكن غالبًا تحويل EXISTS مترابط يتحقق من وجود تطابق واحد على الأقل إلى INNER JOIN.

لكن انتبه: قد ينتج الربط صفوفًا خارجية مكررة إذا طابقت عدة صفوف داخلية الصف نفسه. أضف DISTINCT أو استخدم التجميع لاستعادة صف واحد لكل مفتاح في الجهة الخارجية.

-- Correlated EXISTS
SELECT c.customer_id, c.name
FROM customers c
WHERE EXISTS (SELECT 1 FROM orders o
             WHERE o.customer_id = c.customer_id);

-- Join rewrite (DISTINCT avoids dupes from fan-out)
SELECT DISTINCT c.customer_id, c.name
FROM customers c
JOIN orders o ON o.customer_id = c.customer_id;

مأزق تضاعف الصفوف

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

إذا نفذت خطوة لاحقة COUNT(*) أو SUM(amount) على نتيجة الربط هذه من دون التجميع بعناية، فستكون الأرقام خاطئة.

اسأل دائمًا: هل يمكن للربط أن يضاعف الصفوف؟ إذا كانت الإجابة نعم، فاستخدم DISTINCT أو GROUP BY لدمجها مجددًا.

النمط 2: تحويل NOT EXISTS إلى LEFT JOIN / IS NULL

تُعد إعادة كتابة الربط المضاد نمطًا شائعًا جدًا في المقابلات. يتحول NOT EXISTS المترابط إلى LEFT JOIN يكون فيه الجانب الأيمن NULL.

تحصل الصفوف الخارجية غير المطابقة على قيم NULL في الجانب الأيمن؛ ويؤدي التصفية بحثًا عن تلك القيمة إلى الإبقاء على الصفوف التي لا تطابق شيئًا بالضبط.

-- Correlated NOT EXISTS
SELECT c.customer_id FROM customers c
WHERE NOT EXISTS (SELECT 1 FROM orders o
                  WHERE o.customer_id = c.customer_id);

-- LEFT JOIN / IS NULL rewrite
SELECT c.customer_id
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.customer_id
WHERE o.customer_id IS NULL;

اختَر عمودًا لا يقبل NULL لاختباره

في إعادة الكتابة باستخدام LEFT JOIN / IS NULL، اختبر عمودًا من الجانب الأيمن يكون غير NULL مطلقًا عند وجود تطابق حقيقي، ويفضّل أن يكون مفتاح الربط أو المفتاح الأساسي.

إذا اختبرت عمودًا يقبل NULL، فلن تتمكن من التمييز بين عدم تطابق حقيقي (عدم وجود صف) وصف مطابق تكون قيمته في ذلك العمود NULL فحسب. ويؤدي هذا الخطأ إلى إرجاع صفوف غير صحيحة.

يضمن استخدام مفتاح الربط (هنا o.customer_id) أو o.order_id أن تعني NULL عدم وجود صف مطابق.

النمط 3: من التجميع القياسي إلى JOIN + GROUP BY

يمكن تحويل تجميع مترابط في SELECT إلى ربط مع استعلام فرعي مُجمّع (جدول مشتق).

احسب التجميع لكل مجموعة مرة واحدة، ثم اربطه مجددًا بصفوف التفاصيل. وهكذا يُنفَّذ الاستعلام الداخلي مرة واحدة بدلًا من تنفيذه لكل صف.

-- Correlated scalar aggregate
SELECT e1.name,
       (SELECT MAX(e2.salary) FROM employees e2
        WHERE e2.dept_id = e1.dept_id) AS dept_max
FROM employees e1;

-- Join + GROUP BY rewrite
SELECT e.name, m.dept_max
FROM employees e
JOIN (SELECT dept_id, MAX(salary) AS dept_max
      FROM employees GROUP BY dept_id) m
  ON m.dept_id = e.dept_id;

النمط 4: إعادة الكتابة باستخدام دالة نافذة

غالبًا ما تكون أنظف إعادة كتابة هي استخدام دالة نافذة. MAX(salary) OVER (PARTITION BY dept_id) تستبدل التجميع المترابط بالكامل، من دون الحاجة إلى ربط.

تحسب قيمة المجموعة في مرور واحد وتحافظ على كل صف من صفوف التفاصيل. وهذا هو الحل الذي يرغب المحاورون عادةً في رؤيته أكثر من غيره في استعلامات التحليلات.

SELECT name,
       dept_id,
       salary,
       MAX(salary) OVER (PARTITION BY dept_id) AS dept_max
FROM employees;

إعادة كتابة اختيار أكبر N لكل مجموعة

يمكن إعادة كتابة استعلام فرعي مترابط يختار الصف الأعلى لكل مجموعة (salary = MAX per dept) بطريقة أنيقة باستخدام ROW_NUMBER.

قسّم حسب المجموعة، ورتّب حسب المقياس، واحتفظ بالرتبة 1. واستخدم RANK بدلًا من ذلك إذا أردت الاحتفاظ بكل الصفوف العليا المتعادلة.

SELECT name, dept_id, salary
FROM (
    SELECT name, dept_id, salary,
           ROW_NUMBER() OVER (PARTITION BY dept_id
                              ORDER BY salary DESC) AS rn
    FROM employees
) t
WHERE rn = 1;

متى لا ينبغي إعادة الكتابة

ليست إعادة الكتابة مفيدة دائمًا. احتفظ بالاستعلام الفرعي المترابط عندما:

  • تكون مجموعة الصفوف الخارجية صغيرة، بحيث تكون تكلفة التنفيذ لكل صف ضئيلة.
  • يكون العمود المترابط مفهرسًا جيدًا، ويحوّله المحسّن أصلًا إلى semi-join فعّال.
  • تكون قابلية القراءة أهم من التحسينات الدقيقة في الشيفرة التي تتم صيانتها.

تحوّل المحسّنات الحديثة غالبًا EXISTS إلى semi-join تلقائيًا. اذكر أنك ستقوم بقياس الأداء باستخدام EXPLAIN قبل افتراض أن إعادة الكتابة ستفيد.

التحقق من التكافؤ

بعد أي إعادة كتابة، تحقّق من أنها تُعيد الصفوف نفسها والعدد نفسه مقارنةً بالأصل.

  • تحقّق من تطابق أعداد الصفوف.
  • تحقّق من عدم إدخال تكرارات بسبب تضاعف الصفوف الناتج عن الربط.
  • تحقّق من استمرار السلوك الصحيح في حالات NULL والمجموعات الفارغة.

طريقة سريعة: نفّذ النسختين وطبّق EXCEPT في كلا الاتجاهين؛ وتعني النتيجة الفارغة أنهما متكافئتان. ويقدّر المحاورون تحقّقك بدلًا من افتراضك.

SELECT customer_id FROM query_a
EXCEPT
SELECT customer_id FROM query_b;
-- and the reverse; both empty => equivalent

إعادة كتابة IN باستخدام JOIN

غالبًا ما يمكن أيضًا إعادة كتابة استعلام فرعي غير مترابط يستخدم IN على شكل ربط، لكن تحذير تضاعف الصفوف نفسه ينطبق هنا. إذ يزيل IN تكرارات القيم التي تتحقق عضويتها، بينما لا يفعل الربط ذلك.

إذا احتوت القائمة الداخلية على مفاتيح مكررة، فسيكرر الربط الصفوف الخارجية. استخدم DISTINCT في الجانب الداخلي أو في النتيجة النهائية لمطابقة دلالات IN.

-- IN subquery
SELECT c.name FROM customers c
WHERE c.customer_id IN (SELECT o.customer_id FROM orders o);

-- Join rewrite, de-duplicated to match IN
SELECT DISTINCT c.name
FROM customers c
JOIN orders o ON o.customer_id = c.customer_id;

تحقق سريع

اختر إعادة الكتابة الصحيحة باستخدام الربط لاستعلام الربط المضاد المترابط الذي يعتمد على NOT EXISTS.

مراجعة: إعادة كتابة الاستعلامات الفرعية المترابطة باستخدام عمليات الربط

أهم النقاط:

  • EXISTS → INNER JOIN (أضف DISTINCT لتجنب تكرارات تضاعف الصفوف).
  • NOT EXISTS → LEFT JOIN ... WHERE key IS NULL (اختبر عمودًا لا يقبل NULL).
  • التجميع القياسي المترابط → صِل JOIN بجدول مشتق مُجمّع، أو الأفضل استخدام دالة نافذة.
  • الأعلى لكل مجموعة → ROW_NUMBER (أو RANK للصفوف المتعادلة).
  • تحقّق من التكافؤ وافحص باستخدام EXPLAIN قبل افتراض أن إعادة الكتابة أسرع.

معرفة الصيغتين ومشكلة تضاعف الصفوف هي بالضبط ما تختبره مقابلات المستوى المتوسط.

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

هل درس «إعادة كتابة الاستعلامات الفرعية المترابطة كعمليات ربط» مجاني؟

نعم — نص درس «إعادة كتابة الاستعلامات الفرعية المترابطة كعمليات ربط» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Coding Interview Prep، انتقل إلى CoddyKit PRO. تتضمن دورة Coding Interview Prep 4 دروس في المجموع.

ماذا ستتعلم في «إعادة كتابة الاستعلامات الفرعية المترابطة كعمليات ربط»؟

تحويل المنطق المترابط إلى عمليات ربط أو دوال نافذة لتحسين الأداء تتمرن على Coding Interview Prep مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Coding Interview Prep؟

لا تُشترط خبرة سابقة. Coding Interview Prep على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.

كم من الوقت يستغرق درس «إعادة كتابة الاستعلامات الفرعية المترابطة كعمليات ربط»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Coding Interview Prep هذا؟

نعم. كل درس في Coding Interview Prep يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. تشريح استعلام فرعي مترابط
  2. تجميعات كل مجموعة دون GROUP BY
  3. EXISTS وNOT EXISTS المترابطان
  4. إعادة كتابة الاستعلامات الفرعية المترابطة كعمليات ربط
← العودة إلى Coding Interview Prep