انتقل إلى محتوى الدرس
الدرس 32
دورة MySQL

تصفية المجموعات بجملة HAVING والفرق الدقيق بينها وبين WHEREMySQL HAVING Clause vs WHERE Clause

تعلّم تصفية المجموعات بجملة HAVING والفرق الدقيق بينها وبين WHERE بأسلوب MySQL 8 الحديث مع أمثلة استعلامات SQL وتطبيقات تفاعلية.
أقسام الدرس
6
الأمثلة
6
اللغة
العربية
تاريخ النشر
غير محدد
المشاهدات
7
محتوى الدرس
في هذا الدرس 6تصفّح المواضيع
افهم سبب رفض جملة WHERE للدوال التجميعية بناءً على الترتيب المنطقي لتنفيذ الاستعلام.

من الأخطاء الشائعة محاولة كتابة دالة تجميعية داخل جملة WHERE مثل WHERE COUNT(*) > 5، وهو ما يرفضه محرك MySQL فوراً بالخطأ Error 1111: Invalid use of group function.

  • توقيت التنفيذ المنطقي: تُنفَّذ جملة WHERE على كل صف مفرد قبل الوصول إلى مرحلة GROUP BY وقبل تكوين المجموعات أصلاً.
  • عدم اكتمال الحساب التجميعي: أثناء فحص صف واحد من الجدول، لا يملك المحرك بعدُ قيمة المجموع الكلي SUM() أو العدد النهائي COUNT(*) للمجموعة!

ولذلك وفّرت لغة SQL جملة HAVING المخصصة لتصفية المجموعات بعد اكتمال تجميعها وحساب دوالها.

الفرق الدقيق بين تصفية الصفوف بـ WHERE وتصفية المجموعات بـ HAVING

قاعدة الأداء الذهبية: صفِّ الصفوف مبكراً بجملة WHERE، وخصص جملة HAVING لشروط الدوال التجميعية فقط
مخطط توضيحي لدرس تصفية المجموعات بجملة HAVING والفرق الدقيق بينها وبين WHERE
مخطط توضيحي لدرس تصفية المجموعات بجملة HAVING والفرق الدقيق بينها وبين WHERE
SQL
-- Valid pre-grouping row filter in WHERE (no aggregates)
SELECT dept_id,
       COUNT(*)    AS active_staff,
       AVG(salary) AS avg_salary
FROM employees
WHERE is_active = 1
GROUP BY dept_id;
رشّح المجموعات الملخصة بعد مرحلة GROUP BY باستخدام الشروط التجميعية داخل جملة HAVING.

تُكتب جملة HAVING مباشرةً بعد جملة GROUP BY (وقبل ORDER BY) لتصفية المجموعات الناتجة بناءً على قيم الدوال التجميعية.

  • الفحص بعد اكتمال التجميع: بعد أن تقسم GROUP BY الصفوف وتحسب الدوال التجميعية لكل مجموعة، تحتفظ HAVING فقط بالمجموعات التي يحقق شرطها القيمة الصائبة 1.
  • دمج عدة شروط تجميعية: يمكنك ربط أكثر من دالة تجميعية داخل HAVING باستخدام AND و OR و BETWEEN ومعاملات المقارنة القياسية.

تُستبعد أي مجموعة تفشل في تحقيق شرط HAVING من النتيجة النهائية للاستعلام.

SQL
-- Keep only categories with at least 5 products and high stock
SELECT category,
       COUNT(*)       AS product_count,
       SUM(stock_qty) AS total_stock
FROM products
GROUP BY category
HAVING COUNT(*) >= 5
   AND SUM(stock_qty) >= 100;
اجمع بين جملتي WHERE و HAVING في استعلام واحد لتصفية الصفوف الخام أولاً ثم المجموعات ثانياً.

تتجلى القوة الحقيقية لاستعلامات التحليل عند الجمع بين WHERE و HAVING في استعلام واحد ليؤدي كل منهما دوره الدقيق:

  • الخطوة الأولى WHERE: تصفّي الصفوف الخام من الجدول قبل التجميع (مثل اختيار الطلبات المدفوعة فقط خلال عام 2026).
  • الخطوة الثانية GROUP BY: تجمّع الطلبات المدفوعة المتبقية حسب رقم العميل customer_id وتحسب مجموع مشتريات كل عميل.
  • الخطوة الثالثة HAVING: تصفّي مجموعات العملاء للإبقاء فقط على كبار العملاء الذين بلغ مجموع مشترياتهم 1000.00 فأكثر.

لاحظ أن استبعاد الطلبات الملغاة مبكراً في WHERE يؤثر مباشرةً في دقة المجاميع التي تفحصها HAVING لاحقاً.

SQL
-- VIP customers in 2026: filter rows in WHERE, groups in HAVING
SELECT customer_id,
       COUNT(*)       AS paid_orders_2026,
       SUM(total_usd) AS total_spent_2026
FROM orders
WHERE order_status = 'paid'
  AND created_at >= '2026-01-01'
GROUP BY customer_id
HAVING SUM(total_usd) >= 1000.00
ORDER BY total_spent_2026 DESC;
استخدم الأسماء المستعارة للدوال التجميعية داخل جملة HAVING في MySQL وتعرّف على قاعدة التوافق القياسية.

يدعم محرك MySQL 8 ميزة مريحة تتيح الإشارة إلى الاسم المستعار المعرّف بـ AS للدالة التجميعية في SELECT مباشرةً داخل جملة HAVING.

  • اختصار الكود في MySQL: يمكنك كتابة SUM(total_usd) AS total_spent في العرض ثم كتابة HAVING total_spent >= 500 دون إعادة كتابة الدالة.
  • ملاحظة التوافق القياسي: في معيار ISO SQL الصارم (وبعض المحركات الأخرى)، تُنفَّذ HAVING منطقياً قبل ربط أسماء SELECT المستعارة؛ لذا فإن كتابة الدالة الصريحة HAVING SUM(total_usd) >= 500 يضمن عمل الاستعلام على كافة قواعد البيانات.

داخل بيئة MySQL 8 يعمل كلا الأسلوبين بنفس الكفاءة وخطة التنفيذ تماماً.

SQL
-- Using SELECT aggregate aliases inside HAVING in MySQL 8
SELECT product_id,
       COUNT(*)    AS review_count,
       AVG(rating) AS avg_rating
FROM product_reviews
GROUP BY product_id
HAVING review_count >= 10
   AND avg_rating >= 4.5
ORDER BY avg_rating DESC;
حسّن سرعة تنفيذ الاستعلام واستهلاك الذاكرة بنقل الشروط غير التجميعية من HAVING إلى WHERE.

رغم أن MySQL يسمح نحوياً بكتابة شرط عادي على عمود التجميع داخل HAVING (مثل GROUP BY dept_id HAVING dept_id = 10)، إلا أن ذلك يُعد ممارسة خاطئة تضر بالأداء:

  • تكلفة التصفية المتأخرة: وضع شرط غير تجميعي في HAVING يعني منطقياً تجميع كل أقسام الجدول وحساب مجاميعها في الذاكرة أولاً ثم رميها جميعاً باستثناء القسم رقم 10!
  • سرعة التصفية المبكرة بـ WHERE: كتابة WHERE dept_id = 10 تصفّي الصفوف مبكراً عبر فهرس B-Tree قبل بدء التجميع أصلاً، مما يوفر استهلاك المعالج والذاكرة المؤقتة.

القاعدة الهندسية الثابتة: ضع كل شرط لا يحتوي على دالة تجميعية في WHERE، وخصص HAVING للشروط التجميعية فقط.

SQL
-- Best Practice: filter dept_id early in WHERE, not in HAVING
SELECT dept_id,
       job_role,
       COUNT(*)    AS role_count,
       AVG(salary) AS avg_salary
FROM employees
WHERE dept_id IN (10, 20)
  AND is_active = 1
GROUP BY dept_id, job_role
HAVING COUNT(*) >= 3;
جدول مقارنة شامل بين جملتي WHERE و HAVING من حيث مرحلة التنفيذ ودعم الدوال التجميعية والفهارس.

يوضّح الجدول التالي الفروق الجوهرية الكاملة بين جملتي WHERE و HAVING في MySQL 8:

وجه المقارنةجملة WHEREجملة HAVING
هدف التصفيةالصفوف الفردية الخام في الجدولالمجموعات الملخصة الناتجة عن التجميع
توقيت التنفيذ المنطقيقبل مرحلة GROUP BY والتجميعبعد مرحلة GROUP BY وحساب الدوال
الدوال التجميعيةممنوعة (تُطلق الخطأ Error 1111)مدعومة بالكامل (COUNT و SUM و AVG)
الأسماء المستعارة في SELECTغير متاحة للاستخداممتاحة للاستخدام في MySQL 8
استثمار فهارس B-Treeتستخدم الفهارس مباشرة لتقليص الصفوف المقروءةتفحص نتائج المجموعات المحسوبة في الذاكرة

ملخص الدرس: تعلّمت في هذا الدرس سبب رفض WHERE للدوال التجميعية، وكيفية تصفية المجموعات عبر HAVING، وطريقة الجمع بين WHERE و HAVING في استعلام واحد، وأهمية التصفية المبكرة بـ WHERE لتحسين الأداء.
الخطوة التالية والدرس القادم: نختتم وحدة التجميع في الدرس القادم بتعلّم توليد المجاميع الفرعية والكلية عبر WITH ROLLUP، وتمييز صفوف المجموع بدالة GROUPING()، وإتقان الترتيب المنطقي الكامل لتنفيذ استعلام SQL.

SQL
-- Pipeline: WHERE -> GROUP BY -> HAVING -> ORDER BY -> LIMIT
SELECT category,
       COUNT(*)        AS active_items,
       SUM(stock_qty)  AS category_stock,
       AVG(unit_price) AS avg_category_price
FROM products
WHERE is_active = 1
GROUP BY category
HAVING COUNT(*) >= 3
   AND SUM(stock_qty) >= 50
ORDER BY category_stock DESC
LIMIT 10;
خلاصة المعرفة ومحطة الانتقال

ملخص الدرس

حصاد الدرس

مراجعة مركّزة لأهم المفاهيم والقواعد الهندسية التي أتقنتها في هذا الدرس قبل التطبيق العملي

ماذا تعلّمت في هذا الدرس؟

أتقنّا في هذا الدرس تصفية المجموعات بجملة HAVING والفرق الدقيق بينها وبين WHERE في MySQL 8 مع أمثلة عملية.

الخطوة التالية والدرس القادم

أكمل التدريبات والاختبار التفاعلي أدناه قبل الانتقال إلى الدرس التالي في مسار MySQL.

مشاركة

هل كان هذا الدرس مفيدًا؟

جارٍ تحميل الآراء…

ابنِ لغتك البرمجية

المصطلحات4

راجع المعنى، اشرحه لنفسك، ثم علّم البطاقة كمفهومة.

0 / 4 مفهومةأول بطاقة، أول خطوة
01 HAVING Clause جملة تصفية المجموعات HAVING
جملة في SQL تُنفَّذ بعد GROUP BY لتصفية المجموعات الملخصة بناءً على نتائج الدوال التجميعية.
للمراجعة
02 Pre-Grouping Filter (WHERE) التصفية قبل التجميع (WHERE)
تصفية على مستوى الصفوف الفردية تُنفَّذ قبل GROUP BY لاستبعاد السجلات غير المطلوبة مبكراً باستخدام الفهارس.
للمراجعة
03 Post-Grouping Filter (HAVING) التصفية بعد التجميع (HAVING)
تصفية على مستوى المجموعات تُنفَّذ بعد حساب قيم COUNT و SUM و AVG لكل مجموعة.
للمراجعة
04 MySQL Error 1111 خطأ الاستخدام غير الصالح لدالة التجميع (Error 1111)
رسالة الخطأ التي يُطلقها MySQL عند كتابة دالة تجميعية مباشرة داخل جملة WHERE.
للمراجعة

اقرأ البطاقات بحرية. سجّل الدخول لحفظ تقدم مراجعتك.

حروف قليلة. معنى كامل.

الاختصارات4

راجع المعنى، اشرحه لنفسك، ثم علّم البطاقة كمفهومة.

0 / 4 مفهومةأول بطاقة، أول خطوة
01 KPI KPI
Key Performance Indicator
مؤشر الأداء الرئيسي الذي تُصفّى مجموعاته عبر جملة HAVING
للمراجعة
02 VIP VIP
Very Important Person (High-Value Segment)
شريحة العملاء ذوي القيمة العالية المستخرجة عبر HAVING SUM(total) >= threshold
للمراجعة
03 CPU CPU
Central Processing Unit
وحدة المعالجة المركزية التي نوفر استهلاكها بالتصفية المبكرة في WHERE
للمراجعة
04 ISO ISO
International Organization for Standardization
المنظمة الدولية للمعايير المحددة لترتيب تنفيذ WHERE قبل GROUP BY ثم HAVING
للمراجعة

اقرأ البطاقات بحرية. سجّل الدخول لحفظ تقدم مراجعتك.

من الفهم للتطبيق

مهامك قبل الاختبار

خطوة صغيرة كل مرة. وإنجاز تقدر ترجع له.

0/2مهام منجزة
  1. اكتب استعلام SELECT على جدول employees يعرض dept_id و COUNT(*) باسم emp_count مجمّعةً حسب dept_id، مع الإبقاء فقط على الأقسام التي يزيد عدد موظفيها على 5.

  2. اكتب استعلام SELECT على جدول orders يعرض customer_id و SUM(total_usd) باسم total_spent للطلبات التي تحقق order_status = 'paid'، مجمّعةً حسب customer_id، بشرط ألا يقل المجموع عن 500.

تأشير المهمة إقرار منك بإتمامها، وليس تصحيحًا آليًا للكود. يُحفظ تقدمك في حسابك.

مساحة التحدي

اختبر فهمك.
ووصلها للإتقان.

كل سؤال خطوة لقدّام. اختار، اكتب كود، وشوف نتيجتك فورًا.

8أسئلة 1أنواع تحديات 100%هدف الإتقان
تصحيح تلقائي. فرص بلا حدود.

أكمل المهام، واجمع كل الإجابات الصحيحة. أقل من 100%؟ راجع وأعد المحاولة.

الأسئلة متاحة للاطلاع. سجّل الدخول للتفاعل وحفظ تقدمك.

لمحة عن التحدي8 أسئلة تنتظرك
01

لماذا يفشل الاستعلام SELECT dept_id, COUNT(*) FROM employees WHERE COUNT(*) > 3 GROUP BY dept_id في MySQL؟

تُقيّم الدوال التجميعية بعد تقسيم المجموعات في GROUP BY، ولذلك يجب وضع شروطها في HAVING وليس في WHERE.

02

ما الترتيب المكتوب الصحيح لجمل استعلام SELECT في MySQL؟

تأتي WHERE قبل GROUP BY، وتأتي HAVING بعد GROUP BY، وتختم الجملة بـ ORDER BY ثم LIMIT.

03

لماذا يجب وضع شرط غير تجميعي مثل is_active = 1 في جملة WHERE بدلاً من HAVING؟

التصفية المبكرة في WHERE تستبعد الصفوف غير المطلوبة قبل إرسالها إلى مرحلة التجميع.

04

هل يمكن لجملة HAVING التصفية بدالة تجميعية مثل SUM(salary) > 50000 حتى لو لم تكن SUM(salary) مكتوبة في قائمة SELECT؟

تستطيع جملة HAVING فحص أي دالة تجميعية على المجموعة حتى لو لم ندرج تلك الدالة ضمن الأعمدة المعروضة في SELECT.

05

هل يسمح MySQL 8 باستخدام الاسم المستعار للدالة التجميعية المعرّف في SELECT (مثل COUNT(*) AS cnt) داخل جملة HAVING؟

يتيح MySQL استخدام الأسماء المستعارة المعرّفة في SELECT داخل كل من GROUP BY و HAVING و ORDER BY.

06

في الاستعلام SELECT dept, AVG(pay) FROM staff WHERE pay > 2000 GROUP BY dept HAVING AVG(pay) > 5000، أي موظفين يدخلون في حساب متوسط AVG(pay)؟

تستبعد جملة WHERE الموظفين الذين يقل راتبهم عن أو يساوي 2000 قبل بدء التجميع وحساب المتوسط.

07

هل يمكن ربط شرطين تجميعيين داخل HAVING باستخدام AND أو OR مثل HAVING COUNT(*) >= 3 AND MIN(price) >= 10؟

تقبل جملة HAVING دمج أي عدد من الشروط التجميعية عبر المعاملات المنطقية والأقواس.

08

ماذا يحدث عند استخدام HAVING في استعلام يحتوي على دوال تجميعية بدون جملة GROUP BY؟

عند غياب GROUP BY يشكّل الجدول مجموعة كلية واحدة، فتحتفظ HAVING بصف الملخص إذا تحقق الشرط أو تستبعده.