طَبِّقْهاتعلّم المبدأ، وطبّقه بيدكمقرر 121 · مبادئ قواعد البيانات
الدرس 17 · الوحدة 7: استرجاع وتحليل البيانات

التجميع: GROUP BY و HAVING

من آلاف الصفوف إلى أرقام تساعد على القرار. ⏱ نحو 30 دقيقة.

بعد هذا الدرس ستستطيع أن:
  • تلخّص البيانات بـ COUNT و SUM و AVG و MIN و MAX.
  • تجمع الصفوف بـ GROUP BY وتحسب لكل مجموعة.
  • تفرّق بين WHERE و HAVING.
  • تكتب استعلامًا كاملًا بترتيب أجزائه الصحيح.

١دوال التجميع: من صفوف كثيرة إلى رقم واحد

المدير لا يريد قائمة 12 منتجًا؛ يريد «كم منتجًا لدينا؟ وما متوسط الأسعار؟». دوال التجميع Aggregate Functions تلخّص صفوفًا كثيرة في قيمة واحدة:

الدالةتحسب
COUNT(*)عدد الصفوف
COUNT(col)عدد القيم غير الفارغة في العمودتتجاهل NULL
SUM(col)المجموع
AVG(col)المتوسطتتجاهل NULL
MIN(col) و MAX(col)الأصغر والأكبرتعمل مع النصوص والتواريخ أيضًا
query.sql
SELECT COUNT(*)             AS products,       ROUND(AVG(price), 2) AS avg_price,       MIN(price)           AS cheapest,       MAX(price)           AS dearest,       SUM(price * stock)   AS stock_valueFROM products;
النتيجة · صف واحد
productsavg_pricecheapestdeareststock_value
1260.671215013845
query.sql
SELECT COUNT(*) AS customers, COUNT(phone) AS with_phone, COUNT(city) AS with_cityFROM customers;
النتيجة · صف واحد
customerswith_phonewith_city
1099

COUNT(*) عدّ كل العملاء (10)، و COUNT(phone) تجاهل عبدالله الذي لا جوال له.

٢التجميع حسب مجموعة: GROUP BY

والسؤال الأقوى: «كم منتجًا في كل تصنيف؟». GROUP BY تقسم الصفوف إلى مجموعات حسب قيمة العمود، ثم تحسب دالة التجميع لكل مجموعة:

query.sql
SELECT category, COUNT(*) AS products, ROUND(AVG(price), 1) AS avg_priceFROM productsGROUP BY category;
النتيجة · 5 صفوف
categoryproductsavg_price
أدوات378.3
بهارات241.5
تمور267.5
عسل2105
مشروبات321.7

ومن جدول أصناف الطلبات: كم قطعة بيعت من كل منتج؟ وكم كسبنا منه؟

query.sql
SELECT product_id, SUM(qty) AS units, SUM(qty * price) AS revenueFROM order_itemsGROUP BY product_idORDER BY revenue DESC;
النتيجة · 11 صفًّا
product_idunitsrevenue
34360
52300
17245
63180
72150
121120
2590
9384
8280
11672
10155

٣تصفية المجموعات: HAVING

WHERE تصفّي الصفوف قبل التجميع، و HAVING تصفّي المجموعات بعد التجميع، فتقبل دوال التجميع:

query.sql
-- التصنيفات التي فيها 3 منتجات أو أكثرSELECT category, COUNT(*) AS productsFROM productsGROUP BY categoryHAVING COUNT(*) >= 3;
النتيجة · صفّان
categoryproducts
أدوات3
مشروبات3
query.sql
-- العملاء الذين طلبوا أكثر من مرة، من الطلبات غير الملغاة فقطSELECT customer_id, COUNT(*) AS orders_countFROM ordersWHERE status <> 'ملغى'GROUP BY customer_idHAVING COUNT(*) > 1;
النتيجة · 3 صفوف
customer_idorders_count
13
22
32

WHERE حذفت الطلبات الملغاة أولًا، ثم جُمعت، ثم HAVING أبقت من له أكثر من طلب.

✗ خطأ
query.sql
SELECT category, COUNT(*)FROM productsWHERE COUNT(*) >= 3GROUP BY category;
✗ خطأ (السطر 1): استخدام خاطئ لدالة التجميع COUNT() (هل تحتاج HAVING بدل WHERE؟)
✓ صواب
query.sql
SELECT category, COUNT(*)FROM productsGROUP BY categoryHAVING COUNT(*) >= 3;
النتيجة · صفّان
categoryCOUNT(*)
أدوات3
مشروبات3

٤الترتيب الكامل للاستعلام

الكتابةالتنفيذالوظيفة
SELECT5الأعمدة والحسابات
FROM1الجدول
WHERE2تصفية الصفوف
GROUP BY3تكوين المجموعات
HAVING4تصفية المجموعات
ORDER BY6الترتيب
LIMIT7العدد
🧪 ملعب SQLجرّب: عدد العملاء في كل مدينة. ومتوسط المعدل في كل قسم بقاعدة الكلية (غيّر القاعدة بنسخ الاستعلام إلى مثال college).

توقّع وتحقّق

عدد الصفوف كلها:

مع NULL.

تصفية المجموعات بعد التجميع:

بعد GROUP BY.

أيهما يُنفَّذ أولًا؟

الصفوف قبل المجموعات.

COUNT(phone) حين يكون جوال واحد NULL من 10:

تتجاهل NULL.

الخلاصة

تحدٍّ برمجي: تقرير التصنيفات

متوسط

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

المطلوب:
  • الأعمدة: category، ثم عدد المنتجات، ثم متوسط السعر مقربًا لمنزلة عشرية واحدة.
  • التصنيفات التي فيها أكثر من منتجين فقط.
  • الترتيب حسب المتوسط تنازليًّا.
👀 اعرض النتيجة المطلوبة
النتيجة المطلوبة
categoryproductsavg_price
أدوات378.3
مشروبات321.7
✓ التحقق الآلي من حلّك:
  • الكود يعمل بلا أخطاء
  • التصنيفات الصحيحة بأعدادها ومتوسطاتها بالترتيب الصحيح
  • تستخدم GROUP BY
  • تستخدم HAVING

💡 تلميح 1
ROUND(AVG(price), 1)
💡 تلميح 2
HAVING COUNT(*) > 2 ثم ORDER BY

حاول بنفسك أولًا؛ للمسألة أكثر من حلّ صحيح.

solution.sql
SELECT category, COUNT(*) AS products, ROUND(AVG(price), 1) AS avg_priceFROM productsGROUP BY categoryHAVING COUNT(*) > 2ORDER BY avg_price DESC;
انتقل إلى تقييم الدرساختيار من متعدد، وصح وخطأ، وأكمل الفراغ، وتتبّع الناتج.ابدأ التقييم ←

📝 معلم؟ أرسل ملاحظة للمؤلف عن هذا الدرس