شرط 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
صيغة HAVING في SQL
SELECT column1, aggregate_function(column2)
FROM table_name
WHERE condition_on_rows
GROUP BY column1
HAVING condition_on_groups
ORDER BY column1;
قاعدة ذهبية: استخدم WHERE في SQL لتصفية الصفوف قبل التجميع، واستخدم HAVING في SQL لتصفية المجموعات بعد التجميع.
ترتيب تنفيذ استعلام SQL كامل
ترتيب التنفيذ في 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
جدول employees - بيانات الموظفين
+----+----------+--------+------------+
| 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 في SQL - تصفية الصفوف
-- 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 في SQL - تصفية المجموعات
-- 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: الأقسام التي لديها أكثر من موظفين
HAVING مع COUNT في SQL
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
HAVING مع SUM في SQL
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
تحليل المبيعات بـ HAVING
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
HAVING مع AVG في SQL
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، في أقسام بأكثر من موظف واحد
WHERE + HAVING في SQL
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
HAVING مع AND
-- الأقسام بأكثر من موظفين ومتوسط راتب > 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
HAVING مع OR
-- الأقسام بأكثر من 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: تحليل المبيعات الشهرية
الأشهر بمبيعات > 50000
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: تحليل الأداء الأكاديمي
الصفوف بمتوسط > 75 ونسبة نجاح > 80%
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: تحليل المخزون
الفئات بقيمة مخزون > 10000
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
-- ✅ صحيح: استخدم HAVING للدوال التجميعية
SELECT department, AVG(salary) AS avg_salary
FROM employees
GROUP BY department
HAVING AVG(salary) > 6000;
خطأ 3: الخلط بين WHERE و HAVING
تذكر: استخدم WHERE في SQL لتصفية الصفوف الفردية، واستخدم HAVING في SQL لتصفية المجموعات بعد GROUP BY.

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) وتطوير مهاراتك في قواعد البيانات.

الانتقال إلى الدرس التالي
المحرر الذكي

اكتب الكود وشاهد النتيجة فوراً

جرب الآن مجاناً
قناة ديف عربي

تابع أحدث الدروس والتحديثات مباشرة على واتساب

انضم الآن