خانه/ ایندکس‌گذاری و کارایی/ بهینه‌سازی پرس‌وجو

بهینه‌سازی پرس‌وجو: چطور یک ایندکس موجود را خراب نکنیم

پیشرفته ۱۴ دقیقه مطالعه

داشتن ایندکس درست، فقط نصف راه است. اگر خودِ پرس‌وجو طوری نوشته شده باشد که موتور پایگاه‌داده نتواند از آن ایندکس استفاده کند، هیچ فرقی نمی‌کند چقدر ایندکس ساخته باشی. به این خاصیت — که یک شرط بتواند از ایندکس استفاده کند — SARGable می‌گویند (مخفف Search ARGument ABLE).

اشتباه رایج ۱: پیچاندن ستون ایندکس‌شده در یک تابع

وقتی ستونی که ایندکس دارد را داخل یک تابع (مثل UPPER()، YEAR()، یا هر عملیات ریاضی) می‌گذاری، موتور دیگر نمی‌تواند مستقیم مقدار را در ایندکس جست‌وجو کند — چون باید اول تابع را روی هر ردیف اجرا کند تا ببیند نتیجه‌اش برابر است یا نه. این یعنی ایندکس عملاً بی‌فایده می‌شود. بیا خودمان ببینیم:

امتحانش کن — شرط ساده (SARGable)
امتحانش کن — همان ستون، پیچیده‌شده در تابع (Non-SARGable)
جعبه‌ی اول SEARCH ... USING INDEX است؛ جعبه‌ی دوم — با این‌که همان داده‌ها و همان ایندکس است — به SCAN students برگشت! فقط پیچاندن ستون در UPPER() کافی بود تا ایندکس کاملاً بی‌اثر شود. این دقیقاً همان رفتاری است که در SQL Server هم می‌بینی — مثلاً WHERE YEAR(order_date) = 2024 به‌جای WHERE order_date >= '2024-01-01' AND order_date < '2025-01-01'.

راه‌حل: شرط را SARGable بنویس

به‌جای پیچاندن ستون، مقدار سمت راست را تغییر بده. اگر داده‌ها همیشه با حروف یکسان ذخیره می‌شوند، همان‌طور مقایسه کن؛ اگر روی محدوده‌ی تاریخ کار می‌کنی، از بازه (>= و <) به‌جای تابع روی ستون استفاده کن. قانون کلی: سمت چپِ عملگر مقایسه، همیشه باید ستونِ خالص و دست‌نخورده باشد.

اشتباه رایج ۲: تبدیل ضمنی نوع داده

اگر ستونی از نوع TEXT/NVARCHAR است اما مقدار را بدون کوتیشن یا با نوع عددی مقایسه کنی، موتور ممکن است مجبور شود کل ستون را به نوع دیگری تبدیل کند تا مقایسه کند — که دقیقاً مثل پیچاندن در تابع، ایندکس را غیرقابل‌استفاده می‌کند. همیشه مطمئن شو نوع مقدار سمت راست، دقیقاً با نوع ستون یکی است.

اشتباه رایج ۳: SELECT * به‌جای ستون‌های موردنیاز

در درس ایندکس غیرخوشه‌ای دیدیم Covering Index چطور به موتور اجازه می‌دهد کل پرس‌وجو را از خودِ ایندکس جواب دهد، بدون برگشت به جدول اصلی. اما SELECT * همیشه به همه‌ی ستون‌ها نیاز دارد — یعنی Covering Index هرگز اتفاق نمی‌افتد، حتی اگر ایندکس مناسبی وجود داشته باشد. همیشه فقط ستون‌هایی را انتخاب کن که واقعاً لازم داری.

اشتباه رایج ۴: IN با زیرپرس‌وجوی بزرگ به‌جای EXISTS

در فصل ۵ (زیرپرس‌وجوها) دیدیم NOT IN با یک زیرپرس‌وجوی حاوی NULL می‌تواند نتیجه‌ی نادرست بدهد. از نظر کارایی هم، وقتی زیرپرس‌وجو نتیجه‌ی بزرگی برمی‌گرداند، EXISTS معمولاً کاراتر است — چون به محض پیداکردن اولین تطابق متوقف می‌شود، در حالی که IN ممکن است مجبور شود کل لیست را جمع‌آوری کند.

جمع‌بندی این درس و فصل ۹-۱۰

  • SARGable یعنی شرط، طوری نوشته شده که موتور بتواند مستقیم از ایندکس استفاده کند — تابع یا عملیات روی ستون این قابلیت را از بین می‌برد.
  • تبدیل ضمنی نوع داده، دقیقاً همان اثر مخرب را دارد.
  • SELECT * امکان Covering Index را از بین می‌برد؛ فقط ستون‌های لازم را انتخاب کن.
  • EXISTS برای بررسی وجود رابطه، معمولاً از IN روی زیرپرس‌وجوهای بزرگ کاراتر و ایمن‌تر است.
  • بهترین ابزار تشخیص همیشه یکی است: نقشه‌ی اجرا را باز کن و ببین موتور واقعاً چه کاری انجام می‌دهد، نه چه کاری فکر می‌کنی باید انجام دهد.

با این درس، فصل «ایندکس‌گذاری و کارایی» تمام شد. فصل بعدی سراغ امنیت می‌رود: Login و User، نقش‌ها و مجوزها، و یک نمونه‌ی زنده و واقعی از تزریق SQL.