آموزش ROWS در SQL Server | ۱۰ مثال کاربردی و بهینه‌سازی

آموزش جامع قاب پنجره‌ای ROWS در SQL Server

توسط admin | گروه SQL Server | 1405/04/29

نظرات 0

آموزش جامع قاب پنجره‌ای ROWS در SQL Server با مثال‌های عملی

مقدمه

ROWS قاب پنجره را با شمارش فیزیکی موقعیت ردیف‌ها نسبت به ردیف جاری تعریف می‌کند. این انتخاب برای جمع تجمعی، میانگین متحرک و محاسباتی که دقیقاً به تعداد مشخصی ردیف قبل یا بعد وابسته‌اند شفاف و قابل پیش‌بینی است. در این راهنما رفتار واقعی قاب پنجره‌ای ROWS را از مثال پایه تا سناریوی سازمانی، حالت NULL، خطای رایج و بهینه‌سازی بررسی می‌کنیم.

برای دیدن جایگاه این موضوع در کل خانواده پنجره‌ای، راهنمای جامع عبارت‌های پنجره‌ای SQL Server را نیز مطالعه کنید. هدف این است که Query نهایی هم از نظر معنایی درست و هم در حجم واقعی قابل دفاع باشد.

تعریف، نحو و نوع خروجی

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

نحو استاندارد

aggregate_function(expression) OVER (
    [PARTITION BY partition_expression]
    ORDER BY sort_expression
    ROWS BETWEEN frame_start AND frame_end
)

پارامترها و اجزای مهم

  • تابع پنجره‌ای: تابعی مانند SUM، AVG، ROW_NUMBER، RANK، LAG یا LEAD که نتیجه تحلیلی را تولید می‌کند.
  • PARTITION BY: مرز اختیاری گروه‌های مستقل را تعیین می‌کند و در هر گروه محاسبه از نو آغاز می‌شود.
  • ORDER BY: ترتیب منطقی ردیف‌ها را برای محاسبات وابسته به توالی مشخص می‌کند.
  • Window Frame: در توابع سازگار، محدوده ردیف‌های مؤثر نسبت به ردیف جاری را تعریف می‌کند.

نوع خروجی

ROWS مرز ورودی هر محاسبه را تعیین می‌کند و نوع خروجی همان نوع تابع پنجره‌ای است. مرزها می‌توانند UNBOUNDED PRECEDING، n PRECEDING، CURRENT ROW، n FOLLOWING یا UNBOUNDED FOLLOWING باشند.

مدل ذهنی و ترتیب منطقی اجرا

برای تحلیل قاب پنجره‌ای ROWS ابتدا Dataset پس از FROM، JOIN، WHERE و GROUP BY را در نظر بگیرید. تابع پنجره‌ای روی این مجموعه منطقی محاسبه می‌شود و سپس SELECT خروجی را شکل می‌دهد. به همین دلیل Alias یا خروجی پنجره در WHERE همان سطح قابل استفاده نیست و برای فیلتر باید CTE یا زیرپرس‌وجو ایجاد شود.

سه سؤال پیش از نوشتن Query مطرح کنید: هر محاسبه برای کدام گروه مستقل است، ترتیب دقیق و قطعی ردیف‌ها چیست، و قاب از کجا تا کجا امتداد دارد؟ پاسخ صریح به این سه سؤال بیشتر خطاهای ظریف گزارش‌های تحلیلی را حذف می‌کند.

وجود مقدارهای تکراری و NULL را بخشی از طراحی بدانید. نمونه‌ای که فقط داده یکتا دارد ممکن است در محیط آزمایشی درست به نظر برسد ولی با اولین تساوی در تولید، رتبه یا مانده متفاوتی بسازد. تست کوچک باید عمداً این حالت‌ها را وارد کند.

مثال‌های عملی

مثال 1: جمع از ابتدا تا جاری

قاب صریح تمام ردیف‌های قبلی و جاری را در ترتیب TxnID شامل می‌شود.

SELECT TxnID,Amount,SUM(Amount) OVER(ORDER BY TxnID ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS rt
FROM (VALUES(1,10),(2,20),(3,5)) T(TxnID,Amount);
TxnIDAmountrt
11010
22030
3535

نوشتن BETWEEN خواناتر است؛ شکل کوتاه ROWS UNBOUNDED PRECEDING نیز همین انتهای CURRENT ROW را دارد.

مثال 2: میانگین متحرک سه ردیفی

دو ردیف قبل به‌همراه جاری یک پنجره حداکثر سه‌ردیفی می‌سازد.

SELECT DayNo,Amount,AVG(1.0*Amount) OVER(ORDER BY DayNo ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS MovingAvg
FROM (VALUES(1,10),(2,20),(3,30),(4,40)) D(DayNo,Amount);
DayNoAmountMovingAvg
11010.0
22015.0
33020.0
44030.0

در ابتدای پارتیشن که دو ردیف قبل وجود ندارد، SQL Server فقط ردیف‌های موجود را در میانگین لحاظ می‌کند.

مثال 3: جمع جاری و ردیف بعد

قاب از CURRENT ROW تا یک FOLLOWING برای نگاه کوتاه رو به جلو استفاده می‌شود.

SELECT StepNo,Cost,SUM(Cost) OVER(ORDER BY StepNo ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING) AS CurrentAndNext
FROM (VALUES(1,10),(2,20),(3,30)) S(StepNo,Cost);
StepNoCostCurrentAndNext
11030
22050
33030

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

مثال 4: فیلتر میانگین متحرک

ابتدا مقدار قاب در CTE محاسبه و سپس ردیف‌های با میانگین حداقل 20 انتخاب می‌شوند.

WITH M AS(SELECT DayNo,AVG(1.0*Amount) OVER(ORDER BY DayNo ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) ma FROM (VALUES(1,10),(2,30),(3,40)) D(DayNo,Amount))
SELECT DayNo,ma FROM M WHERE ma>=20;
DayNoma
220.0
335.0

توابع پنجره‌ای در WHERE همان سطح مجاز نیستند؛ CTE مرز محاسبه و فیلتر را درست می‌کند.

مثال 5: پنجره متقارن

یک ردیف قبل، جاری و یک ردیف بعد برای هموارسازی محلی استفاده می‌شود.

SELECT SeqNo,Value,AVG(1.0*Value) OVER(ORDER BY SeqNo ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS LocalAvg
FROM (VALUES(1,10),(2,20),(3,50)) D(SeqNo,Value);
SeqNoValueLocalAvg
11015.0
22026.67
35035.0

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

مثال 6: رفتار NULL در قاب

SUM مقدار NULL را نادیده می‌گیرد ولی موقعیت آن همچنان یک ردیف از قاب ROWS را اشغال می‌کند.

SELECT SeqNo,Value,SUM(Value) OVER(ORDER BY SeqNo ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS PairSum
FROM (VALUES(1,10),(2,NULL),(3,30)) D(SeqNo,Value);
SeqNoValuePairSum
11010
2NULL10
33030

تفاوت «عضویت ردیف» و «مشارکت مقدار» مهم است؛ ROWS ردیف را انتخاب می‌کند و SUM قواعد NULL را اجرا می‌کند.

مثال 7: کل آینده از ردیف جاری

قاب جاری تا انتهای پارتیشن مانده آینده را محاسبه می‌کند.

SELECT InstallmentNo,Amount,SUM(Amount) OVER(ORDER BY InstallmentNo ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) AS Remaining
FROM (VALUES(1,100),(2,80),(3,60)) I(InstallmentNo,Amount);
InstallmentNoAmountRemaining
1100240
280140
36060

این الگو برای مانده اقساط یا ظرفیت آینده مناسب است؛ جهت ترتیب باید با تعریف زمانی کسب‌وکار هماهنگ باشد.

مثال 8: موجودی جاری انبار

ورودی و خروجی علامت‌دار با قاب تجمعی به مانده موجودی تبدیل می‌شوند.

SELECT MoveID,QtyChange,SUM(QtyChange) OVER(ORDER BY MoveDate,MoveID ROWS UNBOUNDED PRECEDING) AS StockBalance
FROM (VALUES(1,CONVERT(date,'2026-01-01'),100),(2,'2026-01-02',-25),(3,'2026-01-02',10)) M(MoveID,MoveDate,QtyChange);
MoveIDQtyChangeStockBalance
1100100
2-2575
31085

MoveID تساوی تاریخ را رفع می‌کند. بدون ترتیب قطعی، مانده میانی در رویدادهای هم‌زمان قابل اتکا نیست.

مثال 9: اصلاح قاب پیش‌فرض

در کلیدهای تکراری قاب پیش‌فرض RANGE می‌تواند همتاها را با هم جمع کند؛ ROWS صریح اثر هر ردیف را جدا می‌کند.

SELECT SeqNo,SortKey,Amount,
 SUM(Amount) OVER(ORDER BY SortKey ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS RowsTotal
FROM (VALUES(1,1,10),(2,1,20),(3,2,5)) D(SeqNo,SortKey,Amount)
ORDER BY SortKey,SeqNo;
SeqNoSortKeyRowsTotal
1110
2130
3235

برای قطعیت کامل SeqNo را نیز در ORDER BY پنجره قرار دهید. مثال، تفاوت معنایی قاب فیزیکی را برجسته می‌کند.

مثال 10: ایندکس برای قاب متحرک

ایندکس روی SensorID، SampleTime و SampleID ترتیب لازم را فراهم می‌کند.

CREATE TABLE #M(SensorID int,SampleTime datetime2,SampleID int,Value decimal(10,2));
INSERT #M VALUES(1,'2026-01-01T00:00:00',1,10),(1,'2026-01-01T00:01:00',2,20);
CREATE INDEX IX_M_Window ON #M(SensorID,SampleTime,SampleID) INCLUDE(Value);
SELECT SampleID,AVG(Value) OVER(PARTITION BY SensorID ORDER BY SampleTime,SampleID ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) ma FROM #M;
DROP TABLE #M;
SampleIDma
110.00
215.00

ایندکس پوششی می‌تواند Lookup و Sort را کم کند، اما نرخ بالای درج حسگرها هزینه نگهداری ایندکس را افزایش می‌دهد.

خطاهای رایج

  • اتکا به قاب پیش‌فرض به‌جای نوشتن ROWS صریح
  • نبود ترتیب قطعی و تغییر عضویت ردیف‌های هم‌ارزش
  • اشتباه در جهت PRECEDING و FOLLOWING
  • ساخت قاب نامعتبر که نقطه شروع بعد از نقطه پایان است

برای عیب‌یابی ابتدا نتیجه را روی Dataset بسیار کوچک دستی محاسبه کنید. سپس Actual Execution Plan را جدا از صحت منطقی بررسی کنید؛ سریع‌بودن Query پاسخ نادرست را قابل قبول نمی‌کند و پاسخ درست با Sort و Spill بزرگ نیز برای تولید آماده نیست.

ملاحظات کارایی

نوشتن قاب ROWS صریح از ابهام قاب پیش‌فرض جلوگیری می‌کند و در برخی برنامه‌ها اجرای جریان‌پذیرتری نسبت به رفتار همتاهای RANGE دارد. با این حال Sort، اندازه پارتیشن و پوشش ایندکس همچنان عامل اصلی هزینه هستند.

آمارهای به‌روز، تخمین درست تعداد ردیف‌ها و Memory Grant کافی روی عملکرد اثر مستقیم دارند. قبل و بعد از تغییر ایندکس، SET STATISTICS IO, TIME ON و Actual Execution Plan را روی حجم نماینده ثبت کنید. از نتیجه یک اجرای گرم یا داده آزمایشی کوچک حکم قطعی نسازید.

ایندکس پیشنهادی باید با بار نوشتن، فضای دیسک و سایر Queryها سنجیده شود. یک ایندکس پوششی عریض ممکن است یک گزارش را سریع کند اما درج و به‌روزرسانی کل سامانه را گران‌تر سازد. Query Store برای مشاهده رفتار در طول زمان و تشخیص Regression مفید است.

بهترین روش‌ها

  • ترتیب و پارتیشن را مستقیماً از تعریف کسب‌وکار استخراج و در مستند فنی ثبت کنید.
  • برای کلیدهای تکراری سیاست روشن داشته باشید و در صورت نیاز کلید یکتای رفع تساوی اضافه کنید.
  • قاب پنجره را صریح بنویسید تا رفتار با تغییر داده یا توسعه Query مبهم نشود.
  • حالت‌های NULL، پارتیشن تک‌ردیفی، پارتیشن بزرگ و داده تکراری را در تست رگرسیون قرار دهید.
  • کارایی را با Plan واقعی و آمار IO و زمان روی حجم نماینده بسنجید، نه با حدس یا فقط Estimated Plan.
  • از Aliasهای توصیفی استفاده کنید تا معنای ستون محاسباتی برای گزارش و API روشن بماند.

سؤالات متداول

برای شروع یادگیری قاب پنجره‌ای ROWS چه پیش‌نیازی لازم است؟

آشنایی با SELECT، مرتب‌سازی، توابع تجمیعی و ترتیب منطقی اجرای Query کافی است. سپس قاب پنجره‌ای ROWS را روی چند ردیف کوچک با مقدار تکراری و NULL آزمایش کنید تا مرز پنجره فقط حفظ نشود، بلکه مشاهده شود.

چگونه صحت نتیجه قاب پنجره‌ای ROWS را با یک تست کوچک بررسی کنیم؟

یک مجموعه داده VALUES با سه تا پنج ردیف بسازید، نتیجه مورد انتظار را دستی حساب کنید و ستون حاصل از قاب پنجره‌ای ROWS را مقایسه نمایید. تست باید حالت عادی، تساوی، NULL و مرز پارتیشن را جداگانه پوشش دهد.

کاربرد تجاری قاب پنجره‌ای ROWS در گزارش مدیریتی چیست؟

قاب پنجره‌ای ROWS امکان ساخت KPI، رتبه‌بندی، روند، سهم از کل و مانده تجمعی را بدون حذف جزئیات فراهم می‌کند. این ویژگی Dataset گزارش را ساده‌تر می‌کند و تعداد رفت‌وبرگشت‌های لایه برنامه به دیتابیس را کاهش می‌دهد.

آیا قاب پنجره‌ای ROWS برای سامانه‌های مالی و فروش مناسب است؟

بله، به شرط آنکه ترتیب قطعی، نوع داده دقیق و مرز محاسبه مستند باشد. در پروژه مالی لازم است خروجی با داده مرجع تطبیق داده شود و برای حجم واقعی، Query Store و Actual Execution Plan نیز بررسی شوند.

تفاوت قاب پنجره‌ای ROWS با GROUP BY یا روش تجمیع سنتی چیست؟

GROUP BY ردیف‌ها را در سطح گروه خلاصه می‌کند، ولی قاب پنجره‌ای ROWS در چارچوب تابع پنجره‌ای معمولاً جزئیات را حفظ می‌کند. انتخاب درست به شکل خروجی نیازمند وابسته است و هیچ‌یک جایگزین مطلق دیگری نیست.

برای پیاده‌سازی حرفه‌ای قاب پنجره‌ای ROWS چه خدماتی مفید است؟

بازبینی مدل داده، طراحی شاخص‌ها، تهیه تست رگرسیون، تحلیل Execution Plan و مشاوره SQL Server بیشترین ارزش را دارند. در پروژه حساس بهتر است منطق قاب پنجره‌ای ROWS همراه تعریف KPI و نمونه مورد انتظار تحویل شود.

رایج‌ترین خطای توسعه‌دهندگان هنگام استفاده از قاب پنجره‌ای ROWS چیست؟

خطای پرتکرار، فرض‌کردن ترتیب یا قاب پیش‌فرض و بی‌توجهی به مقادیر مساوی است. نسخه صحیح باید کلید رفع تساوی، رفتار NULL و تفاوت ترتیب پنجره با ترتیب نهایی را صریح کند.

چگونه کارایی Query دارای قاب پنجره‌ای ROWS را بهبود دهیم؟

ابتدا تعداد ردیف ورودی را با فیلتر SARGable کاهش دهید، سپس ایندکس هماهنگ با پارتیشن و ترتیب را ارزیابی کنید. Sort، Window Spool، Memory Grant و Spill به tempdb در طرح واقعی باید اندازه‌گیری شوند.

بهترین روش نگهداری Queryهای مبتنی بر قاب پنجره‌ای ROWS چیست؟

نام‌گذاری روشن Aliasها، قاب صریح، کامنت درباره سیاست تساوی، تست داده مرزی و ثبت Baseline کارایی بهترین روش است. Query باید در Code Review همراه Execution Plan نمونه و انتظار کسب‌وکار بررسی شود.

قاب پنجره‌ای ROWS در کدام نسخه‌های SQL Server قابل استفاده است؟

قابلیت‌های اصلی Window Functions از SQL Server 2012 به بعد گسترده و پایدارند، هرچند برخی توابع یا بهبودهای Optimizer میان نسخه‌ها تفاوت دارند. Compatibility Level و مستندات نسخه مقصد را پیش از استقرار کنترل کنید.

سؤالات مصاحبه

  1. تفاوت نقش قاب پنجره‌ای ROWS در تعریف پنجره با مرتب‌سازی نهایی نتیجه چیست؟
  2. در چه شرایطی استفاده نادرست از قاب پنجره‌ای ROWS پاسخ تحلیلی را غیرقطعی می‌کند؟
  3. برای بررسی کارایی Query دارای قاب پنجره‌ای ROWS کدام بخش‌های Execution Plan را کنترل می‌کنید؟
  4. چگونه یک تست کوچک برای اثبات رفتار قاب پنجره‌ای ROWS در حضور مقادیر تکراری می‌نویسید؟
  5. چه ایندکسی می‌تواند Sort و خواندن داده در سناریوی قاب پنجره‌ای ROWS را کاهش دهد؟

چک‌لیست نهایی

  • صحت پارتیشن تأیید شده است.
  • ORDER BY قطعی و سیاست تساوی مشخص است.
  • قاب پیش‌فرض یا صریح آگاهانه انتخاب شده است.
  • نتیجه NULL و داده مرزی تست شده است.
  • Actual Execution Plan و Spill بررسی شده است.
  • ایندکس پیشنهادی با هزینه نوشتن سنجیده شده است.

جمع‌بندی

قاب پنجره‌ای ROWS زمانی قابل اعتماد است که مرز داده، ترتیب و رفتار ردیف‌های هم‌ارزش صریح باشند. مثال‌های این مقاله نشان دادند چگونه از پاسخ ساده به Query قابل استقرار برسیم و هم‌زمان صحت، خوانایی و کارایی را کنترل کنیم.

برای مقایسه این قابلیت با سایر اجزای پنجره‌ای، به مقاله مادر Window Clauses در SQL Server بازگردید و نمونه‌ها را روی ساختار جدول واقعی پروژه خود بازنویسی کنید.

 

0 نظر

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

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

حرف 500 حداکثر

اطلاعات تماس

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