0Pricing
SQL Academy · درس

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

استعلام فرعي يعتمد على الصف الخارجي

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

ما هو الاستعلام الفرعي المترابط؟

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

يجعله ذلك قويًا لإجراء المقارنات صفًا بصف، لكنه أيضًا أعلى تكلفة من الاستعلامات الفرعية البسيطة.

الاستعلام الفرعي العادي مقابل المترابط

يتمثل الفرق الأساسي في أن الاستعلام الفرعي العادي لا يحتوي على أي مرجع إلى الاستعلام الخارجي ويمكنه العمل بصورة مستقلة. أما الاستعلام الفرعي المترابط فيعتمد على الصف الخارجي؛ ويمكنكم رؤية الاسم المستعار للجدول الخارجي داخل الاستعلام الفرعي.

في المثال التالي، يشير SELECT الداخلي إلى e1.department_id من الاستعلام الخارجي، مما ينشئ الارتباط.

-- Regular subquery (runs once)
SELECT name, salary
FROM employees
WHERE salary > (SELECT AVG(salary) FROM employees);

-- Correlated subquery (runs once per outer row)
SELECT name, salary, department_id
FROM employees e1
WHERE salary > (
  SELECT AVG(salary)
  FROM employees e2
  WHERE e2.department_id = e1.department_id
);

إعداد الجداول النموذجية

لننشئ جدولين سنستخدمهما طوال هذا الدرس: employees وdepartments. ويوفر هذان الجدولان بيانات واقعية لعرض الاستعلامات الفرعية المترابطة في سيناريوهات مختلفة.

CREATE TABLE departments (
  id   INT PRIMARY KEY,
  name VARCHAR(50)
);

CREATE TABLE employees (
  id            INT PRIMARY KEY,
  name          VARCHAR(50),
  department_id INT REFERENCES departments(id),
  salary        NUMERIC(10,2),
  hire_date     DATE
);

INSERT INTO departments VALUES (1,'Engineering'),(2,'Sales'),(3,'HR');

INSERT INTO employees VALUES
  (1,'Alice',  1, 90000, '2020-03-01'),
  (2,'Bob',    1, 75000, '2021-06-15'),
  (3,'Carol',  2, 60000, '2019-01-10'),
  (4,'David',  2, 68000, '2022-09-01'),
  (5,'Eve',    3, 55000, '2020-07-20'),
  (6,'Frank',  1, 95000, '2018-11-05'),
  (7,'Grace',  3, 52000, '2023-02-28'),
  (8,'Henry',  2, 71000, '2021-04-12');

الموظفون الذين يتجاوز راتبهم متوسط رواتب أقسامهم

من حالات الاستخدام التقليدية للاستعلامات الفرعية المترابطة: العثور على كل موظف يتجاوز راتبه متوسط رواتب قسمه. يعيد الاستعلام الداخلي حساب متوسط القسم لكل صف موظف في الاستعلام الخارجي.

SELECT e1.name, e1.department_id, e1.salary
FROM employees e1
WHERE e1.salary > (
  SELECT AVG(e2.salary)
  FROM employees e2
  WHERE e2.department_id = e1.department_id
)
ORDER BY e1.department_id, e1.salary DESC;

استخدام الاستعلامات الفرعية المترابطة في SELECT

لا تقتصر الاستعلامات الفرعية المترابطة على عبارة WHERE، بل يمكن أن تظهر أيضًا في قائمة SELECT لحساب قيمة لكل صف. نجلب هنا راتب كل موظف إلى جانب متوسط الراتب في قسمه، وكل ذلك في استعلام واحد.

SELECT
  e.name,
  e.salary,
  (
    SELECT ROUND(AVG(e2.salary), 2)
    FROM employees e2
    WHERE e2.department_id = e.department_id
  ) AS dept_avg_salary
FROM employees e
ORDER BY e.department_id, e.name;

العثور على الموظف الأعلى أجرًا في كل قسم

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

SELECT e1.name, e1.department_id, e1.salary
FROM employees e1
WHERE e1.salary = (
  SELECT MAX(e2.salary)
  FROM employees e2
  WHERE e2.department_id = e1.department_id
)
ORDER BY e1.department_id;

استخدام EXISTS مع استعلام فرعي مترابط

غالبًا ما يُستخدم العامل EXISTS مع الاستعلامات الفرعية المترابطة. ويعيد القيمة TRUE إذا أنتج الاستعلام الداخلي صفًا واحدًا على الأقل. نسرد هنا جميع الأقسام التي تضم موظفًا واحدًا على الأقل تم تعيينه قبل عام 2021.

SELECT d.name AS department
FROM departments d
WHERE EXISTS (
  SELECT 1
  FROM employees e
  WHERE e.department_id = d.id
    AND e.hire_date < '2021-01-01'
);

استخدام NOT EXISTS مع استعلام فرعي مترابط

يمثل NOT EXISTS النقيض؛ إذ يعيد القيمة TRUE عندما لا يعثر الاستعلام الفرعي المترابط على أي صفوف مطابقة. ويفيد ذلك في العثور على السجلات الأصلية التي لا تحتوي على سجلات فرعية، مثل الأقسام التي لا تضم موظفين.

SELECT d.name AS department_without_employees
FROM departments d
WHERE NOT EXISTS (
  SELECT 1
  FROM employees e
  WHERE e.department_id = d.id
);

استعلام فرعي مترابط في UPDATE

تعمل الاستعلامات الفرعية المترابطة داخل عبارات UPDATE أيضًا. يضيف المثال التالي العمود dept_avg، ثم يستخدم استعلامًا فرعيًا مترابطًا لملئه بمتوسط راتب قسم كل موظف.

ALTER TABLE employees ADD COLUMN dept_avg NUMERIC(10,2);

UPDATE employees e1
SET dept_avg = (
  SELECT ROUND(AVG(e2.salary), 2)
  FROM employees e2
  WHERE e2.department_id = e1.department_id
);

SELECT name, salary, dept_avg FROM employees ORDER BY department_id, name;

استعلام فرعي مترابط في DELETE

يمكنكم أيضًا استخدام استعلام فرعي مترابط في عبارة DELETE لحذف الصفوف استنادًا إلى بيانات من جدول مرتبط. يحذف الاستعلام أدناه الموظفين الذين تقل رواتبهم عن 60% من متوسط رواتب أقسامهم، وهو نمط لتنظيف البيانات.

DELETE FROM employees e1
WHERE e1.salary < (
  SELECT AVG(e2.salary) * 0.60
  FROM employees e2
  WHERE e2.department_id = e1.department_id
);

SELECT name, salary, department_id FROM employees ORDER BY department_id;

نصيحة لتحسين الأداء: الاستعلام الفرعي المترابط مقابل JOIN

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

-- Correlated version (potentially slower)
SELECT e1.name, e1.salary
FROM employees e1
WHERE e1.salary > (
  SELECT AVG(e2.salary)
  FROM employees e2
  WHERE e2.department_id = e1.department_id
);

-- Equivalent JOIN + derived table (often faster)
SELECT e.name, e.salary
FROM employees e
JOIN (
  SELECT department_id, AVG(salary) AS avg_sal
  FROM employees
  GROUP BY department_id
) dept_avg ON dept_avg.department_id = e.department_id
WHERE e.salary > dept_avg.avg_sal;

تحقق سريع

اختبروا مدى فهمكم للاستعلامات الفرعية المترابطة.

مراجعة: الاستعلامات الفرعية المترابطة

تعلمتم في هذا الدرس أن الاستعلام الفرعي المترابط يشير إلى عمود من الاستعلام الخارجي، ويُعاد تقييمه لكل صف خارجي. أهم النقاط:

  • يمكن أن تظهر هذه الاستعلامات في SELECT وWHERE وUPDATE وDELETE.
  • يتكامل EXISTS وNOT EXISTS بصورة طبيعية مع الاستعلامات الفرعية المترابطة لاختبار وجود صفوف مرتبطة.
  • تتميز هذه الاستعلامات بالمرونة، لكنها قد تكون بطيئة؛ لذا فكروا في إعادة كتابتها باستخدام JOIN عندما يكون الأداء مهمًا.
  • إن وجود الاسم المستعار للجدول الخارجي داخل الاستعلام الفرعي هو ما ينشئ الارتباط.

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

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

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

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

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

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

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

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

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

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

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

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

  1. الاستعلامات الفرعية المترابطة
  2. EXISTS وNOT EXISTS
  3. IN مقابل ANY مقابل ALL
  4. أداء EXISTS مقابل JOIN
← العودة إلى SQL Academy