0Pricing
Coding Interview Prep · درس

أعلى دخل في كل قسم

دمج التقسيم مع الترتيب لحل مسائل أعلى N للرواتب حسب المجموعة

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

من الترتيب العام إلى الترتيب لكل مجموعة

التصعيد التالي هو: «أوجد الموظف الأعلى أجرًا في كل قسم». يجمع هذا السؤال بين الترتيب والتجميع، وهو سؤال مضمون تقريبًا في مقابلات المستوى المتوسط.

افترض وجود جدول employee يحتوي على id وname وdepartment_id وsalary. نريد موظفًا واحدًا، أو أكثر عند التعادل، صاحب أعلى راتب في كل قسم، وليس القيمة العظمى على مستوى الجدول كله.

الأداة الجديدة الأساسية هي PARTITION BY، التي تعيد بدء الترتيب داخل كل قسم.

CREATE TABLE employee (
  id            INT PRIMARY KEY,
  name          VARCHAR(100),
  department_id INT,
  salary        INT
);

تعيد PARTITION BY ضبط الترتيب

إن إضافة PARTITION BY department_id إلى النافذة تخبر قاعدة البيانات بأن تحسب الترتيب بشكل مستقل داخل كل قسم.

يبدأ كل قسم بترتيبه الخاص رقم 1. لذلك يحصل صاحب أعلى راتب في القسم 1 وصاحب أعلى راتب في القسم 5 على الترتيب 1. ومن دون التقسيم، سيحصل صاحب أعلى راتب على مستوى الجدول كله فقط على الترتيب 1.

SELECT name, department_id, salary,
       DENSE_RANK() OVER (
         PARTITION BY department_id
         ORDER BY salary DESC
       ) AS rnk
FROM employee;

التصفية على الترتيب 1

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

يعني استخدام DENSE_RANK (أو RANK) هنا أنه إذا تعادل موظفان في أعلى راتب داخل قسم ما، فسيُرجع الاستعلام كليهما. وهذا هو التفسير الصحيح عادةً لعبارة «الموظف الأعلى أجرًا».

SELECT name, department_id, salary
FROM (
  SELECT name, department_id, salary,
         DENSE_RANK() OVER (
           PARTITION BY department_id
           ORDER BY salary DESC
         ) AS rnk
  FROM employee
) t
WHERE rnk = 1;

ROW_NUMBER عندما تريد صفًا واحدًا بالضبط

أحيانًا يريد المحاور صفًا واحدًا بالضبط لكل قسم حتى عند وجود تعادل. عندها استخدم ROW_NUMBER وأضف معيارًا حتميًا لكسر التعادل، مثل استخدام المعرّف الأصغر.

من دون معيار كسر التعادل، تُحسم حالات التعادل بطريقة عشوائية، وتصبح النتيجة غير حتمية. تؤدي إضافة , id ASC إلى جعل الاختيار قابلًا للتكرار.

SELECT name, department_id, salary
FROM (
  SELECT name, department_id, salary,
         ROW_NUMBER() OVER (
           PARTITION BY department_id
           ORDER BY salary DESC, id ASC
         ) AS rn
  FROM employee
) t
WHERE rn = 1;

DENSE_RANK مقابل ROW_NUMBER مقابل RANK هنا

اختر بناءً على الصياغة الدقيقة للمطلوب:

  • DENSE_RANK = 1: جميع الموظفين المتعادلين في أعلى راتب لكل قسم.
  • RANK = 1: مطابق لـ DENSE_RANK في المرتبة الأولى (تظهر الفجوات فقط بعد المرتبة الأولى).
  • ROW_NUMBER = 1: موظف واحد بالضبط لكل قسم، مع كسر التعادل وفقًا لـ ORDER BY الذي تحدده.

ذكر الخيار الذي اخترته وسبب اختياره هو الجزء الذي يقيّمه المحاورون.

النهج المترابط السابق للدوال النافذة

قبل ظهور الدوال النافذة، كان الحل القياسي هو الاستعلام الفرعي المترابط: الاحتفاظ بالصف فقط إذا لم يكن هناك موظف آخر في القسم نفسه يكسب أكثر.

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

SELECT e.name, e.department_id, e.salary
FROM employee e
WHERE e.salary = (
  SELECT MAX(e2.salary)
  FROM employee e2
  WHERE e2.department_id = e.department_id
);

نهج الربط باستخدام GROUP BY

نمط آخر قابل للنقل بين الأنظمة: احسب أعلى راتب لكل قسم باستخدام GROUP BY، ثم أجرِ الربط مرة أخرى للحصول على الموظفين المطابقين.

هذا النهج فعّال وواضح. يعيد الربط كل موظف يساوي راتبه أعلى راتب في قسمه، ولذلك يحافظ على حالات التعادل.

SELECT e.name, e.department_id, e.salary
FROM employee e
JOIN (
  SELECT department_id, MAX(salary) AS max_sal
  FROM employee
  GROUP BY department_id
) m
  ON e.department_id = m.department_id
 AND e.salary = m.max_sal;

أعلى N لكل قسم

يمتد هذا النمط إلى مسألة «أعلى 3 رواتب لكل قسم» من دون أفكار جديدة. ما عليك سوى تغيير عامل التصفية إلى نطاق.

باستخدام DENSE_RANK، يعيد rnk <= 3 أعلى ثلاثة مستويات رواتب مميزة (وقد يعيد أكثر من ثلاثة صفوف عند وجود تعادل). وباستخدام ROW_NUMBER، يعيد rn <= 3 ثلاثة صفوف بالضبط لكل قسم.

SELECT name, department_id, salary
FROM (
  SELECT name, department_id, salary,
         DENSE_RANK() OVER (
           PARTITION BY department_id
           ORDER BY salary DESC
         ) AS rnk
  FROM employee
) t
WHERE rnk <= 3;

مثال تطبيقي

القسم 1: Ana براتب 120، وBob براتب 120، وCara براتب 90. القسم 2: Dan براتب 200، وEve براتب 150.

  • DENSE_RANK = 1: Ana (120) وBob (120) من القسم 1؛ وDan (200) من القسم 2. ثلاثة صفوف.
  • ROW_NUMBER = 1 مع معيار كسر التعادل باستخدام id: أحد Ana أو Bob (بحسب من يملك المعرّف الأقل) بالإضافة إلى Dan. صفّان.

البيانات نفسها، لكن عدد الصفوف يختلف باختلاف الدالة. اختر ما يطابق السؤال.

تضمين الأقسام وربط الأسماء

غالبًا ما يضيف المحاورون جدول department ويطلبون اسم القسم. ما عليك سوى ربطه بعد تنفيذ الترتيب.

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

SELECT d.name AS department, t.name AS employee, t.salary
FROM (
  SELECT name, department_id, salary,
         DENSE_RANK() OVER (
           PARTITION BY department_id ORDER BY salary DESC
         ) AS rnk
  FROM employee
) t
JOIN department d ON d.id = t.department_id
WHERE t.rnk = 1;

أخطاء ينبغي تجنبها

أخطاء شائعة في الترتيب لكل مجموعة:

  • نسيان PARTITION BY وتنفيذ الترتيب على مستوى جميع البيانات، مما يعيد صاحب أعلى راتب في الشركة فقط.
  • استخدام ROW_NUMBER عندما يوحي السؤال بضرورة إظهار جميع المتعادلين، مما يؤدي إلى إسقاط أصحاب أعلى الرواتب المتعادلين من دون تنبيه.
  • محاولة وضع الدالة النافذة مباشرةً في WHERE بدلًا من تغليفها.
  • ربط جدول القسم قبل تنفيذ الترتيب، مما يؤدي عن طريق الخطأ إلى تغيير مستوى التقسيم.

تحقق سريع

اختر دالة الترتيب المناسبة للمطلوب.

خلاصة

إن إيجاد صاحب أعلى راتب لكل قسم هو نمط الترتيب العام مع إضافة PARTITION BY department_id:

  • DENSE_RANK = 1 يعيد جميع أصحاب أعلى الرواتب المتعادلين في كل قسم.
  • ROW_NUMBER = 1 مع معيار لكسر التعادل يعيد موظفًا واحدًا بالضبط لكل قسم.
  • البدائل القابلة للنقل بين الأنظمة: استخدام MAX مترابط لكل قسم، أو حساب أعلى قيمة باستخدام GROUP BY ثم ربطها مرة أخرى بالجدول.

للتوسّع إلى أعلى N، غيّر = 1 إلى <= N. واذكر بوضوح كيف اخترت التعامل مع حالات التعادل.

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

هل درس «أعلى دخل في كل قسم» مجاني؟

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

ماذا ستتعلم في «أعلى دخل في كل قسم»؟

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

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

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

كم من الوقت يستغرق درس «أعلى دخل في كل قسم»؟

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

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

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

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

  1. ثاني أعلى راتب بخمس طرق
  2. القيمة الأعلى رقم N باستخدام DENSE_RANK
  3. أعلى دخل في كل قسم
  4. إرجاع NULL عند عدم وجود قيمة رقم N
← العودة إلى Coding Interview Prep