تفاوت Clustered Index و NONCLUSTERED INDEX در SQL
📌 تفاوت Clustered Index و Nonclustered Index
(در دو جدول با دادهها و ساختار یکسان)
در این سناریو، دو جدول با ساختار دقیقاً یکسان و دادههای مشابه داریم. تنها تفاوت آنها در نوع ایندکس اعمالشده روی ستون کلیدی OrderId
است:

🧠 تفاوتهای کلیدی:
✅ عملکرد در این مثال خاص:
-
هر دو جدول در تست اجرا شده عملکرد مشابهی داشتند از نظر تعداد ردیفها، هزینه تخمینی اجرا، و نوع عملیات (Index Seek).
-
اما در حالت بهینه (بدون خطای نوع داده یا CONVERT_IMPLICIT
)، ایندکس Nonclustered وقتی Covering باشد، میتواند سریعتر عمل کند چون سبکتر است و نیازی به مراجعه به جدول اصلی ندارد.
-
با این حال، در سناریوهایی که داده زیاد است یا نیاز به مرتبسازی، فیلترگیریهای پشتسرهم یا دسترسی به تمام ستونها باشد، Clustered Index عملکرد بهتری ارائه میدهد.
🔚 نتیجهگیری:
در دو جدول با داده یکسان:
-
اگر فقط تعداد کمی ستون برای خواندن لازم باشد و ایندکس Nonclustered به صورت Covering طراحی شده باشد، Nonclustered Index سریعتر و سبکتر عمل میکند.
-
اما اگر دسترسی به تمام ستونها یا مرتبسازی موردنیاز باشد، Clustered Index به دلیل ساختار ذخیرهسازی خود، پرفورمنس بهتری دارد.
مثال
use tempdb
go
create database [TestTuningDB]
go
use [TestTuningDB]
go
CREATE TABLE Orders1 (
OrderId INT PRIMARY KEY,
Amount DECIMAL(10,2),
GoodsStatus nvarchar(10));
GO
CREATE TABLE Orders2 (
OrderId INT PRIMARY KEY,
Amount DECIMAL(10,2),
GoodsStatus nvarchar(10));
GO
CREATE NONCLUSTERED INDEX IX_Orders1_OrderId
ON Orders1 (OrderId)
INCLUDE (Amount, GoodsStatus);
GO
INSERT INTO Orders1 (OrderId, Amount, GoodsStatus) VALUES
(1, 1200.50, N'OK'),
(2, 500.00, N'WAIT'),
(3, 1200.50, N'OK'),
(4, 200.00, N'CANCEL'),
(5, 850.75, N'WAIT'),
(6, 1500.00, N'OK'),
(7, 750.00, N'CANCEL'),
(8, 400.00, N'WAIT'),
(9, 1200.50, N'OK'),
(10, 950.00, N'SENT');
GO
INSERT INTO Orders2 (OrderId, Amount, GoodsStatus) VALUES
(1, 1200.50, N'OK'),
(2, 500.00, N'WAIT'),
(3, 1200.50, N'OK'),
(4, 200.00, N'CANCEL'),
(5, 850.75, N'WAIT'),
(6, 1500.00, N'OK'),
(7, 750.00, N'CANCEL'),
(8, 400.00, N'WAIT'),
(9, 1200.50, N'OK'),
(10, 950.00, N'SENT');
GO
خلاصهی اطلاعات تصویر:
ویژگی / مقایسه
|
NonClustered Seek (چپ)
|
Clustered Seek (راست)
|
Actual Rows Read
|
10
|
10
|
Actual Rows Returned
|
3
|
3
|
Estimated Operator Cost
|
0.0032853 (100%)
|
0.0032853 (100%)
|
Estimated I/O Cost
|
0.003125
|
0.003125
|
Estimated CPU Cost
|
0.0001603
|
0.0001603
|
Seek Predicate
|
= @orderId (درست و مستقیم)
|
= @orderId
|
Row Size
|
34 B
|
34 B
|
Execution Count
|
1
|
1
|
✅ نتیجه: تقریباً عملکردشون یکسانه، ولی...
✨ برتری کوچک NonClustered در این سناریو چون:
- ایندکس NonClustered در اینجا Covering Index هست (یعنی تمام فیلدهای موردنیاز توی INCLUDE هستند) بنابراین نیازی به رفتن سراغ Heap یا Clustered Data Row نیست.
- این یعنی: I/O واقعیاش در اکثر سیستمها ممکنه کمی کمتر باشه، مخصوصاً وقتی حجم دیتا زیاد باشه یا ایندکس کلاستر سنگین باشه.

پس کدوم بهتره؟
✅ در این وضعیت خاص:
از نظر تئوری و پرفورمنس ریز NonClustered Index Seek بهتر عمل کرده. چرا؟
- چون فقط از یه ساختار سبکتر (ایندکس جداگانه) خونده.
- از جدول اصلی دیتایی نکشیده.
- دقیقاً همون 3 ردیف رو سریع پیدا کرده.
اگه ایندکس NonClustered نبود Covering، مجبور میشد بره Lookup بزنه، که اون موقع Clustered برتری داشت.
جمعبندی نهایی:
مورد
|
NonClustered Index Seek
|
Clustered Index Seek
|
استفاده از ایندکس سبک
|
✅
|
❌
|
نیاز به Lookup
|
❌
|
❌
|
Rows Returned
|
3
|
3
|
هزینهها (همه برابر بودن)
|
=
|
=
|
برتری پرفورمنسی واقعی؟
|
✅(لبهی خیلی کوچیک)
|
تقریباً برابر
|
اگر حجم دیتا زیادتر بود یا تعداد ستونهای جدول زیاد، تفاوت به نفع NonClustered با ایندکس Covering بیشتر مشخص میشد.