MySQL: شرح مفصل لاستعلامات MySQL الفرعية وعبارة EXISTS
آخر تحديث: 2026-08-26
الاستعلامات الفرعية هي إحدى ميزات لغة SQL التي تتيح التداخل — حيث تُستخدم نتائج استعلام ما كشرط لاستعلام آخر.
يقدم هذا الدرس شرحًا منهجيًّا لأشكال الاستعلامات الفرعية المختلفة وكيفية تحسينها.
graph TB
A[Subquery Categories] --> B[Tag-Based Query<br/>Return a single value]
A --> C[Liezi Query<br/>Return a column]
A --> D[Row-based queries<br/>Return a line]
A --> E[Table Child Query<br/>Back to Table]
C --> C1[IN / NOT IN]
C --> C2[ANY / ALL]
E --> E1[FROM Derivation Table]
A --> F[EXISTS<br/>Existence Check]
A --> G[Correlated Subqueries<br/>Citation Format]
1. ما ستتعلمه
- استعلام مفهرس (يعرض قيمة واحدة)
- استعلام قائم على العمود (يعرض عمودًا واحدًا)
- استعلام قائم على الصفوف (يعرض صفًا واحدًا)
- استعلام فرعي من نوع الجدول (يعيد جدولاً)
- موجود/غير موجود
2. سيناريوهات واقعية
(1) المشكلة: لا يمكن تحقيق ذلك في خطوة واحدة
للعثور على «الموظفين الذين تزيد رواتبهم عن متوسط الراتب»، عليك أولاً حساب المتوسط ثم إجراء المقارنة.
(2) حلول للاستعلامات الفرعية
SELECT * FROM employees
WHERE salary > (SELECT AVG(salary) FROM employees);
3. الاستعلام الكمومي القياسي
إرجاع صف واحد وعمود واحد.
▶ مثال: الاستعلام المُعلَّم
-- Employees with above-average salaries
SELECT * FROM employees
WHERE salary > (SELECT AVG(salary) FROM employees);
-- The highest-paid employee
SELECT * FROM employees
WHERE salary = (SELECT MAX(salary) FROM employees);
-- Customers with the most recent orders
SELECT * FROM customers
WHERE id = (SELECT customer_id FROM orders ORDER BY order_date DESC LIMIT 1);
4. استفسارات حول كتاب «ليزي»
تُرجع عمودًا واحدًا يحتوي على عدة صفوف. تُستخدم بالاقتران مع IN و NOT IN و ANY و ALL.
▶ مثال: الاستعلام الفرعي IN
-- Customers with orders
SELECT * FROM customers
WHERE id IN (SELECT DISTINCT customer_id FROM orders);
-- Customers with no orders
SELECT * FROM customers
WHERE id NOT IN (SELECT DISTINCT customer_id FROM orders WHERE customer_id IS NOT NULL);
▶ مثال: الاستعلامات الفرعية ANY/ALL
-- Salary higher than Sales Any employee in the department(Higher than the minimum)
SELECT * FROM employees
WHERE salary > ANY (SELECT salary FROM employees WHERE department = 'Sales');
-- Salary higher than Sales All employees in the department(Higher than the highest)
SELECT * FROM employees
WHERE salary > ALL (SELECT salary FROM employees WHERE department = 'Sales');
5. الاستعلام الفرعي EXISTS
تحقق مما إذا كان الاستعلام الفرعي يُرجع أي نتائج.
▶ مثال: EXISTS
-- Customers with orders
SELECT * FROM customers c
WHERE EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.id);
-- Customers with no orders
SELECT * FROM customers c
WHERE NOT EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.id);
(1) مقارنة بين EXISTS و IN
| البعد | EXISTS | IN |
|---|---|---|
| طريقة التنفيذ | مقارنة الاستعلام الفرعي بالجدول صفًّا بصف | تنفيذ الاستعلام الفرعي أولاً، ثم إجراء المقارنة |
| حالات الاستخدام | جدول خارجي صغير، استعلام فرعي كبير | جدول خارجي كبير، استعلام فرعي صغير |
| التعامل مع القيم NULL | الأمان | فخ "NOT IN" مع القيم NULL |
| الأداء | أفضل بشكل عام | أفضل عندما تكون مجموعة نتائج الاستعلام الفرعي صغيرة |
6. استعلامات الجداول الفرعية (الجداول المشتقة)
الاستعلامات الفرعية كجداول مؤقتة.
▶ مثال: الاستعلام الفرعي FROM
-- The highest-paid person in each department
SELECT e.* FROM employees e
INNER JOIN (
SELECT department, MAX(salary) AS max_salary
FROM employees
GROUP BY department
) dept_max ON e.department = dept_max.department AND e.salary = dept_max.max_salary;
-- Customer Order Statistics
SELECT c.name, order_stats.total_orders, order_stats.total_amount
FROM customers c
INNER JOIN (
SELECT customer_id, COUNT(*) AS total_orders, SUM(amount) AS total_amount
FROM orders
GROUP BY customer_id
) order_stats ON c.id = order_stats.customer_id;
7. الاستعلامات الفرعية المترابطة
تشير الاستعلامات الفرعية إلى حقول من الجدول الخارجي.
▶ مثال: الاستعلامات الفرعية المترابطة
-- The highest-paid employee in each department
SELECT * FROM employees e1
WHERE salary = (
SELECT MAX(salary) FROM employees e2
WHERE e2.department = e1.department
);
-- Employees whose salaries are higher than the department average
SELECT * FROM employees e1
WHERE salary > (
SELECT AVG(salary) FROM employees e2
WHERE e2.department = e1.department
);
8. تحسين الاستعلامات الفرعية
| الاستراتيجية | الوصف |
|---|---|
| استخدم عمليات «JOIN» بدلاً من الاستعلامات الفرعية | عادةً ما يستطيع مُحسِّن MySQL التعامل مع هذا الأمر، لكن عمليات «JOIN» أكثر سهولة في الاستخدام |
| استخدم EXISTS بدلاً من IN | عادةً ما تكون EXISTS أسرع عند التعامل مع مجموعات البيانات الكبيرة |
| تجنب الاستعلامات الفرعية المترابطة | أعد صياغة الاستعلام باستخدام JOIN كلما أمكن ذلك |
| فهرسة الاستعلامات الفرعية | فهرسة حقول الربط/التصفية في الاستعلامات الفرعية |
❓ أسئلة شائعة
NOT IN (1, 2, NULL) تُرجع دائمًا قيمة NULL. استخدم NOT EXISTS بدلاً من ذلك.📖 ملخص
- استعلام الكمية القياسية يُرجع قيمة واحدة؛ قارن باستخدام
= / > / < - استعلام Liezi يُرجع عدة صفوف؛ استخدم
IN / NOT IN / ANY / ALL - EXISTS تتحقق مما إذا كان الاستعلام الفرعي يُرجع أي نتائج؛ وهي عادةً أكثر كفاءة من IN
- الاستعلام الفرعي: يُستخدم في جملة
FROMكجدول مشتق - الاستعلام الفرعي المرتبط يشير إلى حقل من جدول خارجي ويتم تنفيذه مرة واحدة لكل صف
- مبادئ التحسين: استخدام JOIN بدلاً من الاستعلامات الفرعية، استخدام EXISTS بدلاً من NOT IN
📝 تمارين
-
المشكلة الأساسية (الصعوبة ⭐): استخدم الاستعلامات الفرعية للعثور على الموظفين الذين تزيد رواتبهم عن متوسط الراتب.
-
مشكلة متقدمة (مستوى الصعوبة: ⭐⭐): استخدم
EXISTSللعثور على العملاء الذين لم يسبق لهم تقديم أي طلب. -
سؤال التحدي (مستوى الصعوبة: ⭐⭐⭐): استخدم استعلام فرعي مرتبط لتحديد الموظف الذي يحصل على أعلى راتب في كل قسم.