خانه/ تجمیع داده‌ها/ HAVING

HAVING: فیلتر کردن گروه‌ها

مبتدی ۱۲ دقیقه مطالعه

در درس قبل، GROUP BY ردیف‌ها را دسته‌بندی کرد. اما گاهی می‌خواهیم فقط خود گروه‌ها را فیلتر کنیم — مثلاً «فقط دسته‌هایی که بیش از ۲ محصول دارند» یا «فقط شهرهایی که بیش از یک دانش‌آموز دارند». این‌جا WHERE کافی نیست، چون WHERE روی ردیف‌های خام کار می‌کند، نه روی نتیجه‌ی تجمیع‌شده.

چرا WHERE نمی‌تواند نتیجه‌ی COUNT را فیلتر کند

بیا اول ببینیم چرا این‌کار جواب نمی‌دهد:

sql
SELECT category, COUNT(*) AS ProductCount
FROM sales
WHERE COUNT(*) > 2
GROUP BY category;
-- خطا: نمی‌توان از تابع تجمیعی داخل WHERE استفاده کرد

علتش همان ترتیب پردازش منطقی است: WHERE پیش از GROUP BY اجرا می‌شود، یعنی وقتی نوبت به بررسی شرط WHERE می‌رسد، هنوز هیچ گروهی ساخته نشده و COUNT(*) اصلاً معنایی ندارد. بیا این خطا را با چشم خودت ببینی:

این یکی خطا می‌دهد — ببین چرا
خطایی که دیدی («misuse of aggregate») دقیقاً همان چیزی است که در SQL Server هم رخ می‌دهد — این یکی از معدود قوانینی است که SQLite هم به همان سخت‌گیری SQL Server رعایت می‌کند.

راه درست: HAVING

HAVING دقیقاً همان کار WHERE را انجام می‌دهد، با یک تفاوت مهم: بعد از GROUP BY اجرا می‌شود، پس کاملاً می‌تواند به نتیجه‌ی توابع تجمیعی دسترسی داشته باشد:

sql
SELECT category, COUNT(*) AS ProductCount
FROM sales
GROUP BY category
HAVING COUNT(*) > 2;

بیا این نسخه‌ی درست را امتحان کنیم:

امتحانش کن — فیلتر گروه‌ها با HAVING

WHERE و HAVING با هم — دو محل فیلتر متفاوت

می‌توان هر دو را در یک پرس‌وجو با هم آورد. WHERE ردیف‌های خام را قبل از گروه‌بندی فیلتر می‌کند؛ HAVING خودِ گروه‌های ساخته‌شده را بعد از آن فیلتر می‌کند:

sql
SELECT category, SUM(price * quantity) AS Revenue
FROM sales
WHERE quantity IS NOT NULL       -- فیلتر ردیف‌ها، قبل از گروه‌بندی
GROUP BY category
HAVING SUM(price * quantity) > 300000  -- فیلتر گروه‌ها، بعد از گروه‌بندی
ORDER BY Revenue DESC;

بیا این را کامل امتحان کنیم:

امتحانش کن — WHERE و HAVING با هم

ترتیب کامل و نهایی بندهای یک پرس‌وجو

حالا که همه‌ی بندهای اصلی SQL را دیده‌ای، بیا ترتیب نوشتاری کامل (که باید دقیقاً به همین ترتیب در کد نوشته شود) را مرور کنیم:

sql
SELECT ...
FROM ...
WHERE ...        -- فیلتر ردیف‌های خام
GROUP BY ...     -- دسته‌بندی
HAVING ...       -- فیلتر گروه‌ها
ORDER BY ...;    -- مرتب‌سازی نتیجه‌ی نهایی
و ترتیب پردازش منطقی (یعنی چیزی که SQL Server واقعاً پشت صحنه اجرا می‌کند) کمی متفاوت است: FROMWHEREGROUP BYHAVINGSELECTORDER BY. همین تفاوت است که توضیح می‌دهد چرا ORDER BY به نام مستعار SELECT دسترسی دارد ولی WHERE و HAVING ندارند.

جمع‌بندی این درس (و کل فصل ۳)

  • WHERE نمی‌تواند مستقیماً روی نتیجه‌ی توابع تجمیعی شرط بگذارد.
  • HAVING دقیقاً برای همین ساخته شده: فیلتر کردن گروه‌ها بعد از GROUP BY.
  • WHERE و HAVING می‌توانند در یک پرس‌وجو با هم بیایند — هرکدام در جای منطقی خودشان.
  • ترتیب نوشتاری همیشه: SELECT → FROM → WHERE → GROUP BY → HAVING → ORDER BY.

با این درس، فصل ۳ (تجمیع داده‌ها) کامل شد! از فصل بعدی، وارد یکی از مهم‌ترین مفاهیم SQL می‌شویم: ارتباط بین جدول‌ها و JOINها — جایی که یاد می‌گیریم چطور داده‌ی چند جدول مرتبط را با هم ترکیب کنیم.