0Pricing
SQL Interview Prep · درس

التصفية حسب نتيجة دالة نافذة

سبب ضرورة تغليف دالة النافذة في استعلام فرعي أو CTE للتصفية حسبها

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

لماذا لا يمكنك تصفية نافذة في WHERE

هذا من «الفخاخ» الشائعة في المقابلات: تؤدي كتابة WHERE ROW_NUMBER() OVER (...) = 1 إلى حدوث خطأ. لا يُسمح باستخدام دوال النوافذ في WHERE أو GROUP BY أو HAVING.

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

شرح ترتيب التنفيذ

تُحسب دوال النوافذ في مرحلة مخصصة تقع بعد FROM وWHERE وGROUP BY وHAVING، لكنها تقع قبل ORDER BY وLIMIT النهائيين.

لذلك، عند تنفيذ WHERE، لا تكون المرتبة أو رقم الصف موجودًا بعد. ولتصفية هذه القيمة، يجب أن تترك النافذة تكتمل أولًا، ثم تصفي العمود الناتج في طبقة استعلام خارجي.

نمط غلاف الاستعلام الفرعي

الحل المعتاد هو حساب دالة النافذة في استعلام داخلي (جدول مشتق)، وإسناد اسم مستعار إلى النتيجة، ثم تصفية ذلك الاسم المستعار في WHERE الخارجي.

يجب أن يكون للجدول المشتق اسم مستعار (t هنا) — وينتبه المحاورون إلى المرشحين الذين ينسون ذلك. وعندها يصبح rn عمودًا عاديًا يمكن للاستعلام الخارجي مقارنته.

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

نمط CTE (غالبًا أوضح)

ينفذ تعبير الجدول الشائع المهمة نفسها ببنية أكثر قابلية للقراءة. عرّف الترتيب في خطوة WITH، ثم صفِّه في الاستعلام الرئيسي.

وهو مطابق وظيفيًا للاستعلام الفرعي، لكن المحاورين يفضلون عادةً استخدام CTE في البرمجة المباشرة لأن الغرض منه يُقرأ من الأعلى إلى الأسفل.

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

مثال محلول: أفضل N لكل مجموعة

أكثر مسائل النوافذ شيوعًا: «أعلى 3 موظفين راتبًا لكل قسم». احسب الترتيب داخل CTE، ثم احتفظ بالصفوف التي تحقق rn <= 3 خارجه.

اختر دالة الترتيب وفقًا لسلوكها تجاه التعادل: تقيّد ROW_NUMBER النتيجة بثلاثة صفوف بالضبط لكل قسم؛ وانتقل إلى RANK/DENSE_RANK إذا كان يجب تضمين حالات التعادل عند الحد.

WITH ranked AS (
  SELECT department, name, salary,
         ROW_NUMBER() OVER (
           PARTITION BY department ORDER BY salary DESC
         ) AS rn
  FROM employees
)
SELECT department, name, salary
FROM ranked
WHERE rn <= 3
ORDER BY department, rn;

مثال محلول: التصفية حسب المجموع التراكمي

لا يقتصر نمط الغلاف على المراتب. يجب تصفية أي نتيجة نافذة — مثل المجاميع التراكمية والمتوسطات المتحركة وفروق LAG — بالطريقة نفسها.

نحسب هنا رصيدًا تراكميًا، ثم نحتفظ بالصفوف التي تجاوز فيها الرصيد 1000 للمرة الأولى فقط. وتوجد التصفية خارج طبقة النافذة.

WITH balances AS (
  SELECT
    account_id, txn_date, amount,
    SUM(amount) OVER (
      PARTITION BY account_id ORDER BY txn_date
    ) AS running_balance
  FROM transactions
)
SELECT *
FROM balances
WHERE running_balance > 1000;

QUALIFY: الاختصار في بعض قواعد البيانات

توفر Snowflake وBigQuery وTeradata وDuckDB عبارة QUALIFY التي تصفي نتائج النوافذ مباشرةً، من دون الحاجة إلى غلاف. وتُنفَّذ بعد دوال النوافذ، أي في الموضع المطلوب تمامًا.

اذكر QUALIFY لإظهار اتساع معرفتك، لكن وضّح أنها ليست جزءًا من SQL القياسي، ولا تتوفر في PostgreSQL وMySQL وSQL Server، حيث تظل بحاجة إلى غلاف الاستعلام الفرعي أو CTE.

-- Snowflake / BigQuery only:
SELECT department, name, salary
FROM employees
QUALIFY ROW_NUMBER() OVER (
  PARTITION BY department ORDER BY salary DESC
) = 1;

لا تخلط بين HAVING وتصفية النوافذ

يحاول المرشحون أحيانًا استخدام HAVING لتصفية مرتبة. تصفي HAVING المجموعات بعد التجميع باستخدام GROUP BY، وتُنفَّذ مع ذلك قبل دوال النوافذ، ولذلك لا يمكنها أيضًا الإشارة إلى عمود نافذة.

  • WHERE ← تصفي الصفوف قبل التجميع وقبل النوافذ.
  • HAVING ← تصفي المجموعات المجمعة، وما زالت تُنفَّذ قبل النوافذ.
  • تصفية نافذة ← تتطلب استعلامًا خارجيًا (أو QUALIFY).

دمج التصفية المسبقة مع تصفية النافذة

غالبًا ما تحتاج إلى التصفية قبل النافذة وبعدها. طبّق عوامل تصفية الصفوف العادية في WHERE الداخلي، حتى ترى النافذة الصفوف ذات الصلة فقط، ثم صفِّ نتيجة النافذة في الاستعلام الخارجي.

في هذا المثال، نقيّد البيانات أولًا بالموظفين النشطين، ثم نختار صاحب أعلى راتب في كل قسم من بينهم. ويؤدي وضع WHERE active في الداخل إلى تغيير الصفوف التي سيُحسب ترتيبها.

WITH ranked AS (
  SELECT department, name, salary,
         ROW_NUMBER() OVER (
           PARTITION BY department ORDER BY salary DESC
         ) AS rn
  FROM employees
  WHERE is_active = true        -- pre-filter before ranking
)
SELECT * FROM ranked
WHERE rn = 1;                   -- post-filter on the window

ملاحظة حول الأداء

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

لكن توجد ملاحظة مهمة: قد يكون CTE في بعض المحركات حاجزًا أمام التحسين (ويُخزَّن ماديًا)، لذلك قد تكون الاستفادة من جدول مشتق أو QUALIFY أفضل في المسارات شديدة الاستخدام. استخدم EXPLAIN لقياس ذلك إذا كان الأمر مهمًا.

الأخطاء الشائعة

قائمة التحقق النهائية:

  • لا تضع دالة نافذة في WHERE/HAVING مطلقًا؛ فذلك يسبب خطأ.
  • امنح الجدول المشتق اسمًا مستعارًا دائمًا؛ إذ يُرفض الاستعلام الفرعي غير المسمى في FROM.
  • اختر دالة الترتيب وفقًا لسلوك التعادل الذي يتطلبه السؤال.
  • استخدم QUALIFY فقط حيث تكون مدعومة؛ وإلا فارجع إلى غلاف CTE أو الاستعلام الفرعي.

تحقق سريع

لماذا تتطلب تصفية دالة نافذة استخدام غلاف؟

مراجعة: تصفية نتائج النوافذ

لقد اكتملت الآن لديك الصورة الكاملة حول دوال نوافذ الترتيب:

  • تُنفَّذ دوال النوافذ بعد WHERE/GROUP BY/HAVING، لذلك لا يمكنك تصفيتها هناك.
  • غلّف النافذة داخل استعلام فرعي أو CTE (مع إسناد اسم مستعار دائمًا)، ثم صفِّ النتيجة في الاستعلام الخارجي.
  • يتيح ذلك تنفيذ حالات أفضل N لكل مجموعة، وأحدث صف لكل مفتاح، وحدود المجموع التراكمي.
  • يمثل QUALIFY اختصارًا مفيدًا غير قياسي في Snowflake وBigQuery فقط.

أصبحت الآن تمتلك مجموعة أدوات الترتيب الكاملة التي يختبرها المحاورون غالبًا.

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

هل درس «التصفية حسب نتيجة دالة نافذة» مجاني؟

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

ماذا ستتعلم في «التصفية حسب نتيجة دالة نافذة»؟

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

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

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

كم من الوقت يستغرق درس «التصفية حسب نتيجة دالة نافذة»؟

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

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

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

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

  1. OVER وPARTITION BY وORDER BY
  2. ROW_NUMBER للتسلسل الفريد
  3. RANK مقابل DENSE_RANK عند التعادل
  4. التصفية حسب نتيجة دالة نافذة
← العودة إلى SQL Interview Prep