شرط HAVING
جملة HAVING في SQL هي أداة قوية لتصفية نتائج التجميع بعد استخدام GROUP BY. بينما تُستخدم WHERE في SQL لتصفية الصفوف الفردية قبل التجميع، تُستخدم HAVING في SQL لتصفية المجموعات بعد التجميع. في هذا الدرس الشامل من سلسلة تعلم لغة SQL باللغة العربية، سنتعلم الفرق بين WHERE و HAVING في SQL، كيفية استخدام HAVING مع الدوال التجميعية في SQL، وأمثلة عملية متقدمة لتصفية البيانات المجمعة في قواعد البيانات.
1. ما هو HAVING في SQL؟
HAVING هي جملة في لغة SQL تُستخدم لتصفية المجموعات الناتجة من GROUP BY. بينما WHERE في SQL تعمل على الصفوف الفردية، HAVING في SQL تعمل على المجموعات المُجمعة وتسمح باستخدام الدوال التجميعية (COUNT, SUM, AVG, MIN, MAX) في شروط التصفية.
البنية الأساسية لـ HAVING في SQL
SELECT column1, aggregate_function(column2)
FROM table_name
WHERE condition_on_rows
GROUP BY column1
HAVING condition_on_groups
ORDER BY column1;
ترتيب تنفيذ استعلام SQL كامل
1. FROM - اختيار الجدول
2. WHERE - تصفية الصفوف الفردية (قبل التجميع)
3. GROUP BY - تجميع البيانات
4. HAVING - تصفية المجموعات (بعد التجميع) ← هنا!
5. SELECT - اختيار الأعمدة
6. ORDER BY - ترتيب النتائج
7. LIMIT - تحديد عدد النتائج
2. الفرق بين WHERE و HAVING في SQL
الفرق الأساسي بين WHERE و HAVING في SQL هو توقيت التطبيق ونوع البيانات المُصفاة. WHERE في SQL تُصفي الصفوف قبل التجميع، بينما HAVING في SQL تُصفي المجموعات بعد التجميع.
جدول مقارنة شامل: WHERE vs HAVING في SQL
| المعيار | WHERE في SQL | HAVING في SQL |
|---|---|---|
| التوقيت | قبل GROUP BY | بعد GROUP BY |
| يعمل على | الصفوف الفردية | المجموعات المُجمعة |
| الدوال التجميعية | ❌ لا يمكن استخدامها | ✅ يمكن استخدامها |
| الاستخدام مع GROUP BY | اختياري | إلزامي (يتطلب GROUP BY) |
| الأداء | أسرع (تصفية مبكرة) | أبطأ (تصفية متأخرة) |
مثال توضيحي: WHERE vs HAVING في SQL
+----+----------+--------+------------+
| id | name | salary | department |
+----+----------+--------+------------+
| 1 | أحمد | 5000 | IT |
| 2 | فاطمة | 6000 | HR |
| 3 | محمد | 5500 | IT |
| 4 | سارة | 7000 | Sales |
| 5 | علي | 6500 | IT |
| 6 | نورة | 5800 | HR |
| 7 | خالد | 6200 | Sales |
| 8 | ليلى | 4500 | IT |
+----+----------+--------+------------+
-- WHERE: تصفية الموظفين براتب > 5500، ثم التجميع
SELECT
department,
COUNT(*) AS employee_count,
AVG(salary) AS avg_salary
FROM employees
WHERE salary > 5500 -- ← تصفية قبل التجميع
GROUP BY department;
+------------+----------------+------------+
| department | employee_count | avg_salary |
+------------+----------------+------------+
| IT | 1 | 6500.00 | ← علي فقط
| HR | 2 | 5900.00 | ← فاطمة ونورة
| Sales | 2 | 6600.00 | ← سارة وخالد
+------------+----------------+------------+
-- HAVING: التجميع أولاً، ثم تصفية الأقسام التي متوسط رواتبها > 5500
SELECT
department,
COUNT(*) AS employee_count,
AVG(salary) AS avg_salary
FROM employees
GROUP BY department
HAVING AVG(salary) > 5500; -- ← تصفية بعد التجميع
+------------+----------------+------------+
| department | employee_count | avg_salary |
+------------+----------------+------------+
| HR | 2 | 5900.00 |
| Sales | 2 | 6600.00 |
+------------+----------------+------------+
-- IT لم يظهر لأن متوسط رواتبه (5500) ليس أكبر من 5500
3. استخدام HAVING مع COUNT في SQL
أحد أكثر الاستخدامات شيوعاً لـ HAVING في SQL هو تصفية المجموعات حسب عدد العناصر باستخدام دالة COUNT.
مثال 1: الأقسام التي لديها أكثر من موظفين
SELECT
department,
COUNT(*) AS employee_count,
AVG(salary) AS avg_salary
FROM employees
GROUP BY department
HAVING COUNT(*) > 2; -- فقط الأقسام بأكثر من موظفين
+------------+----------------+------------+
| department | employee_count | avg_salary |
+------------+----------------+------------+
| IT | 4 | 5375.00 |
+------------+----------------+------------+
-- فقط IT لديه 4 موظفين (أحمد، محمد، علي، ليلى)
مثال 2: العملاء الذين طلبوا أكثر من 5 مرات
SELECT
customer_name,
COUNT(*) AS total_orders,
SUM(amount) AS total_spent
FROM orders
GROUP BY customer_name
HAVING COUNT(*) > 5
ORDER BY total_orders DESC;
4. استخدام HAVING مع SUM في SQL
يمكنك استخدام HAVING في SQL مع دالة SUM لتصفية المجموعات حسب المجموع الإجمالي، مثل العملاء الذين أنفقوا أكثر من مبلغ معين.
مثال 1: الأقسام بإجمالي رواتب أكبر من 15000
SELECT
department,
COUNT(*) AS employees,
SUM(salary) AS total_payroll
FROM employees
GROUP BY department
HAVING SUM(salary) > 15000
ORDER BY total_payroll DESC;
+------------+-----------+---------------+
| department | employees | total_payroll |
+------------+-----------+---------------+
| IT | 4 | 21500 | ← 5000+5500+6500+4500
+------------+-----------+---------------+
مثال 2: المنتجات بمبيعات تتجاوز 10000
SELECT
product_name,
COUNT(*) AS orders_count,
SUM(quantity * price) AS total_revenue
FROM sales
GROUP BY product_name
HAVING SUM(quantity * price) > 10000
ORDER BY total_revenue DESC;
5. استخدام HAVING مع AVG في SQL
HAVING في SQL مع دالة AVG يُستخدم لتصفية المجموعات حسب المتوسط، مثل الأقسام التي متوسط رواتبها أعلى أو أقل من قيمة معينة.
مثال 1: الأقسام بمتوسط راتب أعلى من 6000
SELECT
department,
COUNT(*) AS employees,
AVG(salary) AS avg_salary,
MIN(salary) AS min_salary,
MAX(salary) AS max_salary
FROM employees
GROUP BY department
HAVING AVG(salary) > 6000;
+------------+-----------+------------+------------+------------+
| department | employees | avg_salary | min_salary | max_salary |
+------------+-----------+------------+------------+------------+
| Sales | 2 | 6600.00 | 6200 | 7000 |
+------------+-----------+------------+------------+------------+
6. دمج WHERE و HAVING في استعلام SQL واحد
يمكنك استخدام WHERE و HAVING معاً في استعلام SQL واحد. WHERE في SQL تُصفي الصفوف قبل التجميع، ثم HAVING في SQL تُصفي المجموعات بعد التجميع.
مثال 1: الموظفون براتب > 5000، في أقسام بأكثر من موظف واحد
SELECT
department,
COUNT(*) AS high_earners,
AVG(salary) AS avg_high_salary
FROM employees
WHERE salary > 5000 -- ← تصفية الصفوف أولاً
GROUP BY department
HAVING COUNT(*) > 1; -- ← ثم تصفية المجموعات
+------------+--------------+------------------+
| department | high_earners | avg_high_salary |
+------------+--------------+------------------+
| IT | 2 | 6000.00 | ← محمد وعلي
| HR | 2 | 5900.00 | ← فاطمة ونورة
| Sales | 2 | 6600.00 | ← سارة وخالد
+------------+--------------+------------------+
مثال 2: مبيعات 2024 للمنتجات بإيرادات > 5000
SELECT
product_name,
COUNT(*) AS orders_count,
SUM(amount) AS total_revenue
FROM orders
WHERE YEAR(order_date) = 2024 -- WHERE: تصفية السنة
GROUP BY product_name
HAVING SUM(amount) > 5000 -- HAVING: تصفية الإيرادات
ORDER BY total_revenue DESC;
7. استخدام شروط متعددة في HAVING
يمكنك استخدام عدة شروط في HAVING في SQL باستخدام AND و OR، تماماً مثل WHERE في SQL.
مثال 1: شروط AND في HAVING
-- الأقسام بأكثر من موظفين ومتوسط راتب > 5500
SELECT
department,
COUNT(*) AS employees,
AVG(salary) AS avg_salary
FROM employees
GROUP BY department
HAVING COUNT(*) > 2 AND AVG(salary) > 5500;
مثال 2: شروط OR في HAVING
-- الأقسام بأكثر من 3 موظفين أو إجمالي رواتب > 15000
SELECT
department,
COUNT(*) AS employees,
SUM(salary) AS total_payroll
FROM employees
GROUP BY department
HAVING COUNT(*) > 3 OR SUM(salary) > 15000;
مثال 3: شروط معقدة في HAVING
SELECT
department,
COUNT(*) AS employees,
AVG(salary) AS avg_salary,
MAX(salary) - MIN(salary) AS salary_range
FROM employees
GROUP BY department
HAVING
AVG(salary) BETWEEN 5500 AND 7000
AND COUNT(*) >= 2
AND MAX(salary) - MIN(salary) < 2000;
8. أمثلة عملية متقدمة لـ HAVING في SQL
مثال 1: تحليل المبيعات الشهرية
SELECT
YEAR(order_date) AS year,
MONTH(order_date) AS month,
COUNT(*) AS total_orders,
SUM(amount) AS monthly_revenue,
AVG(amount) AS avg_order_value
FROM orders
WHERE order_date >= '2024-01-01'
GROUP BY YEAR(order_date), MONTH(order_date)
HAVING SUM(amount) > 50000
ORDER BY year DESC, month DESC;
مثال 2: العملاء VIP (أكثر من 10 طلبات وإنفاق > 5000)
SELECT
customer_name,
COUNT(*) AS total_orders,
SUM(amount) AS total_spent,
AVG(amount) AS avg_order_value,
MAX(order_date) AS last_order
FROM orders
GROUP BY customer_name
HAVING
COUNT(*) > 10
AND SUM(amount) > 5000
ORDER BY total_spent DESC
LIMIT 20;
مثال 3: تحليل الأداء الأكاديمي
SELECT
class,
COUNT(*) AS total_students,
AVG(score) AS class_average,
MIN(score) AS lowest_score,
MAX(score) AS highest_score,
COUNT(CASE WHEN score >= 50 THEN 1 END) AS passed,
ROUND(COUNT(CASE WHEN score >= 50 THEN 1 END) * 100.0 / COUNT(*), 2) AS pass_rate
FROM exam_results
GROUP BY class
HAVING
AVG(score) > 75
AND COUNT(CASE WHEN score >= 50 THEN 1 END) * 100.0 / COUNT(*) > 80
ORDER BY class_average DESC;
مثال 4: تحليل المخزون
SELECT
category,
COUNT(*) AS product_count,
SUM(stock) AS total_stock,
AVG(price) AS avg_price,
SUM(stock * price) AS inventory_value
FROM products
WHERE stock > 0
GROUP BY category
HAVING
SUM(stock * price) > 10000
AND COUNT(*) >= 5
ORDER BY inventory_value DESC;
مثال 5: تقرير الموارد البشرية المتقدم
SELECT
department,
COUNT(*) AS employees,
SUM(salary) AS monthly_payroll,
AVG(salary) AS avg_salary,
MIN(salary) AS min_salary,
MAX(salary) AS max_salary,
MAX(salary) - MIN(salary) AS salary_gap
FROM employees
WHERE status = 'active'
GROUP BY department
HAVING
COUNT(*) >= 3
AND SUM(salary) > 15000
AND AVG(salary) > 5000
ORDER BY monthly_payroll DESC;
9. الأخطاء الشائعة في استخدام HAVING
خطأ 1: استخدام HAVING بدون GROUP BY
-- ❌ خطأ: HAVING بدون GROUP BY
SELECT AVG(salary)
FROM employees
HAVING AVG(salary) > 6000;
-- ERROR: HAVING clause requires GROUP BY
-- ✅ صحيح: إضافة GROUP BY
SELECT department, AVG(salary) AS avg_salary
FROM employees
GROUP BY department
HAVING AVG(salary) > 6000;
-- أو استخدم WHERE إذا لم تحتج GROUP BY
SELECT AVG(salary) AS avg_salary
FROM employees
WHERE salary > 6000;
خطأ 2: استخدام دالة تجميعية في WHERE
-- ❌ خطأ: لا يمكن استخدام AVG في WHERE
SELECT department, AVG(salary)
FROM employees
WHERE AVG(salary) > 6000
GROUP BY department;
-- ERROR: Invalid use of aggregate function
-- ✅ صحيح: استخدم HAVING للدوال التجميعية
SELECT department, AVG(salary) AS avg_salary
FROM employees
GROUP BY department
HAVING AVG(salary) > 6000;
خطأ 3: الخلط بين WHERE و HAVING
10. نصائح وأفضل الممارسات لـ HAVING في SQL
1. استخدم WHERE قبل HAVING لتحسين الأداء
-- سيء: تصفية كل شيء بعد التجميع
SELECT department, AVG(salary)
FROM employees
GROUP BY department
HAVING department != 'Temp';
-- جيد: تصفية قبل التجميع (أسرع)
SELECT department, AVG(salary)
FROM employees
WHERE department != 'Temp'
GROUP BY department;
2. استخدم أسماء واضحة مع AS
يمكنك استخدام الأسماء المستعارة (aliases) في HAVING في SQL لتحسين قابلية القراءة.
3. اجمع شروط HAVING المنطقية
استخدم AND و OR بشكل منطقي لتجميع الشروط المتعلقة في HAVING في SQL.
4. استخدم HAVING فقط للدوال التجميعية
إذا كان الشرط لا يتضمن دالة تجميعية في SQL، استخدم WHERE بدلاً من HAVING لتحسين الأداء.
5. وثّق الاستعلامات المعقدة
-- تقرير: الأقسام الكبيرة والمكلفة
-- الشروط: أكثر من 3 موظفين وإجمالي رواتب > 15000
SELECT
department,
COUNT(*) AS employees,
SUM(salary) AS total_payroll
FROM employees
WHERE status = 'active'
GROUP BY department
HAVING COUNT(*) > 3 AND SUM(salary) > 15000;
ملخص الدرس
- HAVING في SQL: تُستخدم لتصفية المجموعات بعد GROUP BY
- WHERE vs HAVING: WHERE للصفوف قبل التجميع، HAVING للمجموعات بعد التجميع
- الدوال التجميعية: يمكن استخدام COUNT, SUM, AVG, MIN, MAX في HAVING
- شروط متعددة: يمكن استخدام AND و OR في HAVING في SQL
- مع WHERE: يمكن دمج WHERE و HAVING في استعلام SQL واحد
- الترتيب: FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY
- الأداء: استخدم WHERE قبل HAVING عندما يكون ممكناً
- Best Practice: استخدم HAVING فقط للدوال التجميعية في SQL
الخطوة التالية: مقدمة إلى الربط بين الجداول (Joins Introduction)
أكمل رحلتك التعليمية وانتقل إلى الدرس التالي لتعلم مقدمة إلى الربط بين الجداول (Joins Introduction) وتطوير مهاراتك في قواعد البيانات.
الانتقال إلى الدرس التالي