۱۴۰۴/۰۷/۰۴ Nebular

Clustered Index و Non-Clustered Index در SQL Server

بیایم خیلی دقیق و گام به گام Clustered Index و Non-Clustered Index در SQL Server رو بررسی کنیم.


1️⃣ ایندکس (Index) در کل چی هست؟

ایندکس مثل فهرست یک کتاب عمل می‌کنه.

  • وقتی جدولت میلیون‌ها رکورد داره، بدون ایندکس باید کل جدول رو اسکن کنه (Table Scan).
  • با ایندکس می‌تونه خیلی سریع به ردیف موردنظر برسه (Index Seek یا Index Scan).

2️⃣ Clustered Index

  • ساختار داده: داده‌ها در دیسک به ترتیب ایندکس‌شده ذخیره می‌شن.
  • هر جدول فقط یک Clustered Index می‌تونه داشته باشه (چون داده‌ها فقط یکبار می‌تونن مرتب بشن).
  • کلید Clustered Index → داده‌ها بر اساس اون مرتب می‌شن.

📌 به عبارت دیگه:
Clustered Index خود داده‌های جدول هست، فقط مرتب‌شده.

مثال:

CREATE CLUSTERED INDEX IX_Employees_EmpID
ON Employees(EmpID);

حالا رکوردهای جدول Employees بر اساس EmpID روی دیسک مرتب می‌شن.

اگر کوئری بزنی:

SELECT * FROM Employees WHERE EmpID = 100;

SQL Server مستقیم با Index Seek می‌ره روی رکورد 100 → سریع‌ترین حالت.


3️⃣ Non-Clustered Index

  • داده‌های جدول به همون شکل ذخیره می‌مونن (بدون تغییر در ترتیب).
  • ایندکس جداگانه‌ای ساخته می‌شه که شامل کلید ایندکس + Pointer به رکورد اصلی هست.
  • یک جدول می‌تونه چندین Non-Clustered Index داشته باشه.

📌 به عبارت دیگه:
Non-Clustered Index یک نقشه‌ی جداگونه‌ست که اشاره می‌کنه داده کجاست.

مثال:

CREATE NONCLUSTERED INDEX IX_Employees_Name
ON Employees(Name);

حالا اگه بزنی:

SELECT * FROM Employees WHERE Name = 'Ali';

SQL Server می‌ره روی Non-Clustered Index، ردیف‌های مربوط به 'Ali' رو پیدا می‌کنه، بعد با Pointer به داده‌ی اصلی در جدول وصل می‌شه.


4️⃣ تفاوت اصلی

ویژگیClustered IndexNon-Clustered Index
ذخیره‌سازیداده‌ها خودشون مرتب می‌شنساختار جداگونه (Index + Pointer)
تعدادفقط ۱بی‌نهایت
سرعت جستجوسریع‌تر (چون مستقیم به داده می‌ره)یک مرحله اضافی (باید به رکورد اصلی مراجعه کنه)
کاربردبیشتر برای کلید اصلی یا ستون‌های پر استفاده در شرط‌هابرای ستون‌هایی که زیاد توی جستجو/فیلتر/Sort/Join استفاده می‌شن
اندازهمعمولاً بزرگتر (چون شامل کل داده‌هاست)سبک‌تر (فقط کلید + Pointer)

5️⃣ مثال واقعی

جدول زیر رو در نظر بگیر:

CREATE TABLE Orders (
    OrderID INT PRIMARY KEY,   -- به طور پیش‌فرض Clustered Index
    CustomerID INT,
    OrderDate DATE,
    Amount DECIMAL(10,2)
);

🔹 Clustered Index

روی OrderID ساخته شده چون Primary Key هست.
داده‌های جدول روی دیسک به ترتیب OrderID ذخیره شدن.

🔹 Non-Clustered Index

اگر زیاد بخوای بر اساس OrderDate کوئری بزنی:

CREATE NONCLUSTERED INDEX IX_Orders_OrderDate
ON Orders(OrderDate);

حالا کوئری:

SELECT * FROM Orders WHERE OrderDate = '2025-09-26';

→ مستقیم می‌ره روی Non-Clustered Index و بعد با Pointer رکوردها رو برمی‌گردونه.


6️⃣ وقتی Clustered بهتره؟

  • ستون‌هایی که یونیک و افزایشی هستن مثل IDENTITY یا Primary Key.
  • ستون‌هایی که مرتباً برای جستجوهای محدوده‌ای (Range Queries) استفاده می‌شن.
    مثلاً:
SELECT * FROM Orders WHERE OrderID BETWEEN 1000 AND 2000;

7️⃣ وقتی Non-Clustered بهتره؟

  • ستون‌هایی که در شرط WHERE زیاد استفاده می‌شن اما یونیک نیستن.
  • ستون‌هایی که در JOIN یا ORDER BY زیاد به کار می‌رن.
  • مثلاً Name, Email, OrderDate و …

8️⃣ نکات مهم

  • اگر جدول هیچ Clustered Index نداشته باشه → بهش می‌گیم Heap (یعنی داده‌ها بدون ترتیب خاص ذخیره شدن).
  • زیاد بودن ایندکس‌ها سرعت SELECT رو بالا می‌بره ولی UPDATE / INSERT / DELETE رو کند می‌کنه (چون ایندکس‌ها هم باید آپدیت بشن).
  • باید تعادل داشته باشیم و فقط روی ستون‌های مهم ایندکس بذاریم.

🔑 جمع‌بندی کوتاه:

  • Clustered Index = داده مرتب‌شده روی دیسک (یک عدد برای هر جدول)
  • Non-Clustered Index = نقشه‌ی جداگونه با اشاره به داده (می‌تونه زیاد باشه)
  • انتخاب درست ایندکس = تفاوت بین کوئری کند و کوئری سریع 🚀

خیلی خوب، بریم سراغ Execution Plan و اینکه تفاوت Clustered و Non-Clustered رو چطور نشون می‌ده.
من مرحله به مرحله توضیح می‌دم که وقتی یک کوئری رو اجرا می‌کنی، SQL Server دقیقاً چه شکلی Plan می‌سازه.


1️⃣ فعال کردن Execution Plan

قبل از اجرای کوئری، در SSMS گزینه‌ی Include Actual Execution Plan رو بزن (یا کلید ترکیبی Ctrl + M)
بعد کوئری رو اجرا کن.


2️⃣ حالت Clustered Index

فرض کن جدولی داریم:

CREATE TABLE Employees (
    EmpID INT PRIMARY KEY,   -- به طور پیش‌فرض Clustered Index
    Name NVARCHAR(50),
    Salary INT
);

حالا این کوئری رو اجرا کن:

SELECT * FROM Employees WHERE EmpID = 100;

📌 Execution Plan چه چیزی نشون می‌ده؟

  • یک Clustered Index Seek می‌بینی.
  • دلیلش اینه که EmpID کلید Clustered Index هست → مستقیم می‌ره سراغ داده.
  • هزینه (Cost) خیلی پایینه چون عملیات سریع انجام می‌شه.

3️⃣ حالت Non-Clustered Index

حالا یک ایندکس Non-Clustered بسازیم:

CREATE NONCLUSTERED INDEX IX_Employees_Name
ON Employees(Name);

و کوئری بگیریم:

SELECT * FROM Employees WHERE Name = 'Ali';

📌 Execution Plan چه چیزی نشون می‌ده؟

  • اول یک Non-Clustered Index Seek انجام می‌شه روی IX_Employees_Name.
  • بعد می‌بینی یک عملیات به اسم Key Lookup (Clustered) اضافه شده.
  • چرا؟ چون ایندکس فقط شامل Name + Pointer به Clustered Index هست.
    برای آوردن بقیه ستون‌ها (Salary, EmpID و …) باید بره روی Clustered Index و داده کامل رو بیاره.

4️⃣ مقایسه

حالتدر Execution Plan چه می‌بینی؟عملکرد
Clustered Index (روی EmpID)Clustered Index Seekمستقیم داده رو میاره (خیلی سریع)
Non-Clustered Index (روی Name)Non-Clustered Index Seek + Key Lookupاول ایندکس جستجو → بعد مراجعه به Clustered Index

5️⃣ نکته مهم (Covering Index)

اگه بخوای Key Lookup حذف بشه و کل داده‌ها فقط از Non-Clustered Index خونده بشن، باید یک Covering Index بسازی.

مثال:

CREATE NONCLUSTERED INDEX IX_Employees_Name_Salary
ON Employees(Name)
INCLUDE (Salary);

حالا اگر بزنی:

SELECT Name, Salary FROM Employees WHERE Name = 'Ali';

📌 Execution Plan → فقط Non-Clustered Index Seek می‌بینی (بدون Key Lookup).
چون تمام ستون‌های موردنیاز توی خود ایندکس وجود دارن.


جمع‌بندی Execution Plan:

  • Clustered Index → مستقیم Index Seek می‌بینی.
  • Non-Clustered Index → معمولاً Index Seek + Key Lookup.
  • اگر Covering Index بسازی → فقط Index Seek.
Accept Cookies
Accept Cookies
[your-shortcode]