تفاوت های where و having در پایگاه داده SQL Server

بررسی عمیقتر تفاوت where و having در sql server

توسط admin | گروه SQL Server | 1404/01/14

نظرات 0

تفاوت WHERE و HAVING در SQL از دیدگاه سیستمی و عملکردی

در SQL، دستور WHERE و HAVING هر دو برای فیلتر کردن داده‌ها استفاده می‌شوند، اما تفاوت‌های عمیقی در نحوه اجرا، مرحله پردازش، تأثیر بر عملکرد و حتی مکانیزم‌های پیاده‌سازی آن‌ها در موتورهای پایگاه داده وجود دارد. برای درک این تفاوت‌ها، بیایید این دو دستور را از چندین منظر بررسی کنیم.


1. مرحله پردازش در معماری SQL Execution Plan

هر دستور SQL از چندین فاز پردازشی عبور می‌کند، از جمله:

  • Parsing (تجزیه): تحلیل سینتکس و تولید درخت نحوی.

  • Binding (بررسی معنایی): تعیین نوع داده و معتبر بودن جداول و ستون‌ها.

  • Optimization (بهینه‌سازی): تولید و انتخاب بهترین Query Execution Plan (QEP).

  • Execution (اجرا): اجرای کوئری توسط موتور پایگاه داده.

WHERE و HAVING در Execution Plan:

  • WHERE در مرحله‌ی Row Filtering در حین خواندن داده از جداول (Table Scan یا Index Seek) اعمال می‌شود.

  • HAVING بعد از اجرای GROUP BY و محاسبه Aggregate Functions در مرحله Post-Aggregation Filtering اجرا می‌شود.


2. مقایسه WHERE و HAVING در مراحل اجرای Query

WHERE

  • به طور مستقیم روی هر سطر (Row-Level Filtering) اعمال می‌شود.

  • قبل از GROUP BY اجرا می‌شود و فقط روی فیلدهای جدول اصلی کار می‌کند.

  • در فاز Storage Engine (بهینه‌ترین بخش پردازش داده) اجرا می‌شود که باعث می‌شود INDEX Seek/Scan بتواند از آن بهره ببرد.

  • نمی‌توان از Aggregate Functions مثل SUM(), COUNT(), AVG() در WHERE استفاده کرد.

  • فیلترهای WHERE می‌توانند در Index Seek قرار بگیرند که باعث کاهش داده‌های پردازش‌شده و افزایش سرعت اجرا می‌شود.

HAVING

  • روی گروه‌های داده‌ای (Group-Level Filtering) بعد از اعمال GROUP BY کار می‌کند.

  • بعد از WHERE اجرا می‌شود، یعنی در مرحله‌ی Query Processor (منطقه‌ی کم‌بهینه‌تر پردازش) اجرا می‌شود.

  • می‌تواند از Aggregate Functions مانند SUM(), COUNT(), AVG() برای فیلتر کردن گروه‌های داده‌ای استفاده کند.

  • معمولاً منجر به Sort و Hashing پردازشی می‌شود که می‌تواند هزینه اجرای Query را افزایش دهد.

  • در Execution Plan معمولاً در Stream Aggregate Operator یا Hash Aggregate Operator دیده می‌شود که بعد از عملیات تجمیع (Aggregation) اجرا می‌شود.


3. مثال و بررسی Execution Plan

WHERE Example



SELECT EmployeeID, Name, Salary FROM Employees WHERE Salary > 5000;

HAVING Example



SELECT Department, AVG(Salary) AS AvgSalary FROM Employees GROUP BY Department HAVING AVG(Salary) > 5000;

Execution Plan برای WHERE

  1. موتور پایگاه داده بررسی می‌کند که آیا روی Salary ایندکسی وجود دارد یا خیر.

  2. اگر ایندکس وجود داشته باشد، از Index Seek برای پیدا کردن سطرهای مناسب استفاده می‌کند (O(log n) در B-Tree).

  3. اگر ایندکس وجود نداشته باشد، Table Scan اجرا شده و روی هر سطر شرط بررسی می‌شود (O(n)).

Execution Plan برای HAVING

  1. ابتدا یک Table Scan یا Index Scan انجام می‌شود تا تمام داده‌های جدول خوانده شود.

  2. داده‌ها بر اساس GROUP BY Department گروه‌بندی می‌شوند. این کار معمولاً از طریق:

    • Stream Aggregate (بهینه‌تر برای داده‌های مرتب‌شده) یا

    • Hash Aggregate (برای حجم بالای داده) انجام می‌شود.

  3. سپس مقدار AVG(Salary) برای هر گروه محاسبه می‌شود.

  4. بعد از محاسبه Aggregate، شرط HAVING بررسی شده و گروه‌های نامعتبر حذف می‌شوند.


4. بهینه‌سازی و تأثیر عملکردی (Performance Impact)

WHERE سریع‌تر از HAVING است، زیرا:

  • در لایه‌ی Storage Engine اجرا می‌شود که سریع‌تر است.

  • با Index Seek/Scan سازگار است و داده‌های ناخواسته زودتر فیلتر می‌شوند.

  • پردازش روی سطرهای جداگانه انجام می‌شود، نه کل گروه‌های داده‌ای.

HAVING پردازشی سنگین‌تر است، زیرا:

  • بعد از GROUP BY اجرا می‌شود و باید کل داده‌ها پردازش و گروه‌بندی شوند.

  • امکان استفاده مستقیم از ایندکس را ندارد (مگر اینکه ایندکس روی ستون‌های GROUP BY قرار گرفته باشد).

  • در اجرای Query، ممکن است نیاز به Sort و Hash Aggregation باشد که می‌تواند پرهزینه باشد.


5. چگونه WHERE و HAVING را ترکیب کنیم؟

برای بهینه‌سازی، بهتر است ابتدا داده‌ها را با WHERE فیلتر کرده و سپس HAVING را برای اعمال فیلترهای Aggregate استفاده کنیم:

 
SELECT Department, AVG(Salary) AS AvgSalary FROM Employees WHERE Salary > 3000 -- فیلتر اولیه برای کاهش داده‌های پردازشی GROUP BY Department HAVING AVG(Salary) > 5000; -- فیلتر روی Aggregate Function

مزیت این روش:

  • WHERE ابتدا داده‌های نامعتبر را حذف می‌کند، باعث کاهش پردازش GROUP BY می‌شود.

  • سپس HAVING فقط روی گروه‌های مورد نیاز اعمال می‌شود و بار پردازشی را کم می‌کند.


جمع‌بندی نهایی

ویژگی WHERE HAVING
مرحله اجرا قبل از GROUP BY بعد از GROUP BY
سطح فیلتر فیلتر روی سطرها (Row-Level Filtering) فیلتر روی گروه‌ها (Group-Level Filtering)
پشتیبانی از Aggregate Functions ❌ (پشتیبانی نمی‌کند) ✅ (پشتیبانی می‌کند)
سازگاری با ایندکس ✅ (Index Seek/Scan) ❌ (نیاز به پردازش Aggregate)
موقعیت در Execution Plan قبل از Aggregation (Storage Engine) بعد از Aggregation (Query Processor)
تأثیر روی Performance سریع‌تر (کاهش زودهنگام داده‌ها) کندتر (نیاز به Grouping و Aggregation)
موارد استفاده بهینه فیلتر مستقیم روی ستون‌ها فیلتر پس از محاسبات گروه‌بندی شده

نتیجه‌گیری:

  • اگر نیازی به Aggregate ندارید، همیشه از WHERE استفاده کنید.

  • اگر نیاز به Aggregate دارید، بهتر است ترکیبی از WHERE و HAVING را استفاده کنید تا بار پردازشی کاهش یابد.

  • بهینه‌سازی ایندکس‌ها و Query Execution Plan می‌تواند تأثیر زیادی بر عملکرد کوئری‌های شما داشته باشد.

 

بررسی مراحل اجرای یک Query در SQL Execution Plan

۱. Parsing (تجزیه) – بررسی سینتکس SQL و تولید درخت نحوی

Parsing اولین مرحله در پردازش یک کوئری SQL است که در آن موتور پایگاه داده بررسی می‌کند که کوئری از نظر سینتکس صحیح باشد و ساختار مناسبی داشته باشد. در این مرحله، SQL Server یا سایر سیستم‌های مدیریت پایگاه داده (DBMS) کوئری را تجزیه می‌کنند و یک درخت نحوی (Syntax Tree) تولید می‌شود که نشان‌دهنده ترتیب و روابط بین اجزای کوئری است.

مراحل Parsing:

  1. Lexical Analysis (تحلیل واژگانی)

    • کوئری به واحدهای کوچک‌تری به نام توکن (Token) تبدیل می‌شود.

    • مثال: در کوئری SELECT name FROM Employees WHERE age > 30 توکن‌ها شامل SELECT, name, FROM, Employees, WHERE, age, >, 30 هستند.

  2. Syntax Analysis (تحلیل نحوی)

    • بررسی می‌شود که توکن‌ها به درستی مرتب شده‌اند و ساختار کوئری با قواعد گرامری SQL مطابقت دارد.

    • اگر خطایی در سینتکس وجود داشته باشد (مثلاً SELECT * FORM table1 به جای SELECT * FROM table1)، موتور پایگاه داده پیام خطای نحوی نمایش می‌دهد.

  3. Parse Tree (درخت نحوی)

    • اگر کوئری از نظر نحوی صحیح باشد، یک درخت نحوی ساخته می‌شود که روابط بین اجزای کوئری را نشان می‌دهد.

    • این درخت شامل گره‌هایی (Nodes) است که اجزای مختلف مانند SELECT, FROM, WHERE و غیره را مشخص می‌کنند.

چرا Parsing مهم است؟

  • تشخیص زودهنگام خطاهای سینتکسی قبل از اجرای کوئری.

  • ایجاد پایه‌ای برای مراحل بعدی مانند Binding و Optimization.

  • بهبود عملکرد کوئری با ساده‌سازی ساختار ورودی.


۲. Binding (بررسی معنایی) – بررسی نوع داده‌ها و اعتبار جداول و ستون‌ها

بعد از Parsing، مرحله Binding انجام می‌شود که در آن اعتبار اجزای کوئری بررسی می‌شود. حتی اگر کوئری از نظر نحوی صحیح باشد، ممکن است از نظر معنایی نامعتبر باشد.

مراحل Binding:

  1. بررسی وجود جداول و ستون‌ها

    • بررسی می‌شود که جداول و ستون‌های استفاده‌شده در کوئری واقعاً در پایگاه داده وجود دارند.

    • مثال: SELECT salary FROM Employees در حالی که جدول Employees وجود ندارد → پیام خطا دریافت می‌شود.

  2. بررسی نوع داده‌ها (Type Checking)

    • بررسی می‌شود که مقادیر مورد استفاده با نوع داده‌های ستون‌ها سازگار هستند.

    • مثال: WHERE age = 'Ali' در حالی که age یک مقدار عددی است → خطای Type Mismatch رخ می‌دهد.

  3. بررسی مجوزهای دسترسی (Permissions Check)

    • بررسی می‌شود که کاربر دارای مجوز (Permissions) لازم برای دسترسی به جداول و ستون‌های مورد استفاده در کوئری باشد.

  4. بررسی ارتباط بین جداول (Foreign Key Constraints)

    • در صورت وجود رابطه بین جداول، بررسی می‌شود که کلیدهای خارجی (Foreign Keys) رعایت شده باشند.

چرا Binding مهم است؟

  • جلوگیری از اجرای کوئری‌های نامعتبر.

  • اطمینان از اینکه کوئری با ساختار داده‌های پایگاه داده هماهنگ است.

  • جلوگیری از مشکلاتی مانند NULL Reference یا Type Mismatch.


۳. Optimization (بهینه‌سازی) – انتخاب بهترین طرح اجرایی برای کوئری

مرحله بهینه‌سازی (Query Optimization) یکی از مهم‌ترین بخش‌های اجرای کوئری در SQL است. در این مرحله، موتور پایگاه داده چندین Query Plan ممکن برای اجرای کوئری ایجاد می‌کند و بهترین آن را انتخاب می‌کند.

مراحل Optimization:

  1. تولید چندین Execution Plan

    • موتور پایگاه داده چندین روش برای اجرای کوئری تولید می‌کند. این روش‌ها ممکن است شامل Index Seek, Index Scan, Table Scan, Nested Loops, Hash Join و غیره باشند.

  2. محاسبه هزینه اجرای هر Plan

    • برای هر طرح اجرایی، Query Optimizer هزینه تقریبی را محاسبه می‌کند.

    • فاکتورهایی مانند تعداد ردیف‌ها، اندازه داده‌ها، وجود ایندکس و روش‌های JOIN در محاسبه هزینه تأثیر دارند.

  3. انتخاب کارآمدترین طرح (Best Plan Selection)

    • از بین طرح‌های موجود، کم‌هزینه‌ترین طرح انتخاب می‌شود.

    • این طرح ممکن است شامل استفاده از ایندکس‌ها، بهینه‌سازی JOINها، و مرتب‌سازی بهتر داده‌ها باشد.

فاکتورهای مهم در Optimization:

  • ایندکس‌ها: استفاده از Index Seek به جای Table Scan برای کاهش هزینه.

  • پارتیشن‌بندی داده‌ها: تقسیم داده‌های بزرگ به بخش‌های کوچکتر برای پردازش سریع‌تر.

  • کاهش استفاده از DISTINCT و ORDER BY: چون این عملیات نیاز به مرتب‌سازی داده‌ها دارند و هزینه‌بر هستند.

چرا Optimization مهم است؟

  • اجرای سریع‌تر کوئری‌ها و کاهش زمان پردازش.

  • کاهش بار پردازشی روی CPU و حافظه.

  • بهینه‌سازی استفاده از ایندکس‌ها و کاهش هزینه‌های I/O.


۴. Execution (اجرا) – اجرای کوئری با استفاده از بهترین طرح اجرایی

آخرین مرحله، اجرای واقعی کوئری است. در این مرحله، موتور پایگاه داده از طرح اجرایی انتخاب‌شده در مرحله بهینه‌سازی استفاده می‌کند و عملیات خواندن، پردازش و بازگرداندن داده‌ها را انجام می‌دهد.

مراحل Execution:

  1. دریافت Execution Plan نهایی

    • پس از بهینه‌سازی، موتور پایگاه داده طرح اجرایی را دریافت می‌کند.

  2. اجرای عملیات بر اساس Plan

    • اگر کوئری از ایندکس‌ها استفاده کند، موتور پایگاه داده عملیات Index Seek را انجام می‌دهد.

    • اگر از JOIN استفاده شده باشد، عملیات Nested Loop, Hash Join, یا Merge Join اجرا می‌شود.

    • برای مرتب‌سازی، عملیات Sort Operator اجرا می‌شود.

  3. مدیریت حافظه و پردازش داده‌ها

    • SQL Server در حین اجرا از Buffer Pool و Memory Grant برای مدیریت داده‌ها استفاده می‌کند.

    • اگر داده‌ها زیاد باشند، ممکن است از TempDB برای ذخیره موقت اطلاعات استفاده شود.

  4. بازگرداندن نتیجه به کاربر

    • پس از پردازش داده‌ها، نتیجه کوئری به کاربر ارسال می‌شود.

    • اگر کوئری شامل SELECT باشد، ردیف‌های نهایی بازگردانده می‌شوند.

    • اگر کوئری INSERT, UPDATE یا DELETE باشد، تغییرات در پایگاه داده اعمال می‌شوند.

چرا Execution مهم است؟

  • اجرای کارآمدتر و سریع‌تر کوئری‌ها.

  • کاهش مصرف منابع سخت‌افزاری.

  • امکان تحلیل عملکرد کوئری با Execution Plan Analysis.


جمع‌بندی

🔹 Parsing = بررسی سینتکس و ساخت درخت نحوی.
🔹 Binding = بررسی اعتبار جداول، ستون‌ها، و نوع داده‌ها.
🔹 Optimization = انتخاب کارآمدترین روش برای اجرای کوئری.
🔹 Execution = اجرای طرح انتخاب‌شده و بازگرداندن نتیجه به کاربر.

✅ با شناخت این مراحل، می‌توان کوئری‌های بهینه‌تری نوشت و مشکلات عملکردی را به‌خوبی شناسایی و حل کرد. 🚀

 

 

0 نظر

نظر محترم شما در مورد مقاله های وب سایت برنامه نویسی و پایگاه داده

نظرات محترم شما در خدمات رسانی بهتر ما را یاری می نمایند. لطفا اگر مایل بودید یک نظر ما را مهمان فرمائید. آدرس ایمیل و وب سایت شما نمایش داده نخواهد شد.

حرف 500 حداکثر

اطلاعات تماس

  • آدرس:اصفهان-خیابان ام کلثوم غربی - بعد خیابان تخم چی - بیست متر بعد از پیتزا ننه شب - کوچه تعمیر گاه سمار زغالی - پلاک 354 - درب مشکی - طبقه هفتم
  • آدرس ایمیل:najafzade@gmail.com
  • وب سایت:http://www.a00b.com/
  • تلفن ثابت:(+98)9131253620
  • تلفن همراه:09131253620