آموزش تابع CUME_DIST در SQL Server با ۱۰ مثال عملی | آموزش جامع و مثال عملی

آموزش تابع CUME_DIST در SQL Server با ۱۰ مثال عملی

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

نظرات 0

آموزش جامع تابع CUME_DIST در SQL Server با ۱۰ مثال عملی

مقدمه

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

مزیت تابع پنجره‌ای این است که جزئیات هر ردیف حفظ می‌شود و هم‌زمان محاسبه‌ای مبتنی بر ردیف‌های مرتبط در دسترس قرار می‌گیرد. این ویژگی گزارش‌سازی را از Self Joinهای شکننده، Cursor و پردازش تکراری در لایه برنامه بی‌نیاز می‌کند، مشروط بر اینکه ترتیب و پارتیشن درست تعریف شوند.

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

تعریف و منطق تابع CUME_DIST

CUME_DIST برای محاسبه جایگاه تجمعی هر مقدار در توزیع و شناسایی صدک تقریبی استفاده می‌شود. پنجره تحلیل با OVER تعریف می‌گردد و موتور SQL Server نتیجه را بدون حذف ردیف‌های اصلی محاسبه می‌کند. این رفتار تفاوت بنیادی آن با GROUP BY است.

در طراحی حرفه‌ای باید سؤال کسب‌وکار به سه جزء تبدیل شود: مرز گروه با PARTITION BY، ترتیب منطقی با ORDER BY و در صورت نیاز Frame. سپس نوع خروجی، رفتار NULL و مقادیر هم‌رتبه بررسی می‌شود. PERCENT_RANK از رتبه شروع می‌کند؛ CUME_DIST تمام ردیف‌های هم‌رتبه را در صورت کسر می‌آورد.

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

CUME_DIST ( ) OVER ( [ PARTITION BY ... ] ORDER BY order_expression )

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

  • scalar_expression یا عبارت مرتب‌سازی باید نوع داده مناسب و قابل پیش‌بینی داشته باشد.
  • PARTITION BY اختیاری است و در نبود آن، تمام ردیف‌های ورودی یک پارتیشن هستند.
  • ORDER BY ترتیب محاسبه را تعیین می‌کند و بهتر است با یک tie-breaker یکتا کامل شود.
  • Frame در توابع وابسته به مرز پنجره باید صریح نوشته شود؛ رفتار پیش‌فرض همیشه معادل کل پارتیشن نیست.
  • NULL باید با سیاست روشن مدیریت شود؛ صفر، رشته خالی و NULL معنای یکسان ندارند.

نوع خروجی

نوع خروجی CUME_DIST: float بزرگ‌تر از صفر و کوچک‌تر یا مساوی یک. در لایه گزارش، تبدیل به decimal یا متن فقط برای نمایش انجام شود و مقدار خام برای محاسبات بعدی نگه داشته شود.

نکته کلیدی: برای مقادیر مساوی، خروجی یکسان و برابر انتهای گروه هم‌رتبه است؛ این رفتار را با ROW_NUMBER اشتباه نگیرید.

مثال‌های عملی مستقل و قابل اجرا

مثال 1: محاسبه جایگاه نسبی پایه

CUME_DIST جایگاه هر امتیاز را در توزیع مرتب‌شده برمی‌گرداند.

WITH Scores AS (SELECT * FROM (VALUES (1,N'الف',40),(2,N'ب',60),(3,N'پ',60),(4,N'ت',80),(5,N'ث',100))V(Id,Name,Score))
SELECT Id,Name,Score,CUME_DIST() OVER(ORDER BY Score) AS RelativePosition FROM Scores ORDER BY Score,Id;
نامامتیازCUME_DIST
پ600.600000

نکته کاربردی: خروجی float است؛ برای نمایش درصد می‌توان در 100 ضرب کرد ولی دقت محاسباتی را زود گرد نکنید.

مثال 2: تأثیر مقادیر مساوی

دو امتیاز 60 هم‌رتبه‌اند و تابع برای هر دو خروجی یکسان تولید می‌کند.

WITH Scores AS (SELECT * FROM (VALUES (1,N'الف',40),(2,N'ب',60),(3,N'پ',60),(4,N'ت',80),(5,N'ث',100))V(Id,Name,Score))
SELECT Name,Score,RANK() OVER(ORDER BY Score) AS Rnk,CUME_DIST() OVER(ORDER BY Score) AS P FROM Scores ORDER BY Score,Id;
امتیازرتبهنسبت
6020.600000

نکته کاربردی: شناخت رفتار tie برای ساخت Badge، گروه‌بندی مشتری و گزارش منابع انسانی ضروری است.

مثال 3: رتبه‌بندی مستقل در هر گروه

پارتیشن باعث می‌شود توزیع هر دپارتمان با جمعیت خودش سنجیده شود.

WITH D AS (SELECT * FROM (VALUES (1,N'فروش',70),(2,N'فروش',90),(3,N'فنی',60),(4,N'فنی',80))V(Id,Dept,Score))
SELECT Dept,Id,Score,CUME_DIST() OVER(PARTITION BY Dept ORDER BY Score) AS P FROM D ORDER BY Dept,Score;
واحدامتیازجایگاه
فروش901
فنی600.5

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

مثال 4: تبدیل خروجی به درصد خوانا

CAST کنترل‌شده، درصد را برای داشبورد خوانا می‌کند و مقدار خام برای تحلیل حفظ می‌شود.

WITH Scores AS (SELECT * FROM (VALUES (1,N'الف',40),(2,N'ب',60),(3,N'پ',60),(4,N'ت',80),(5,N'ث',100))V(Id,Name,Score))
SELECT Name,Score,CAST(100.0*CUME_DIST() OVER(ORDER BY Score) AS decimal(6,2)) AS PercentValue FROM Scores ORDER BY Score,Id;
امتیازدرصد
100100.00

نکته کاربردی: برای محاسبات بعدی از مقدار float خام و برای لایه نمایش از decimal گرد شده استفاده کنید.

مثال 5: فیلتر افراد بالای آستانه

چون تابع پنجره‌ای در WHERE همان سطح مجاز نیست، CTE ابتدا جایگاه را محاسبه می‌کند.

WITH Scores AS (SELECT * FROM (VALUES (1,N'الف',40),(2,N'ب',60),(3,N'پ',60),(4,N'ت',80),(5,N'ث',100))V(Id,Name,Score))
,W AS(SELECT *,CUME_DIST() OVER(ORDER BY Score) AS P FROM Scores) SELECT Id,Name,Score,P FROM W WHERE P>=0.75 ORDER BY P;
نامامتیازP
ت800.8

نکته کاربردی: آستانه 0.75 باید با تعریف کسب‌وکار هماهنگ شود؛ دو تابع در مرزها ممکن است اعضای متفاوتی انتخاب کنند.

مثال 6: رفتار پارتیشن تک‌ردیفی

این حالت مرزی برای شعبه یا محصول تازه‌وارد اهمیت دارد.

WITH D AS (SELECT * FROM (VALUES (1,N'قدیمی',10),(2,N'قدیمی',20),(3,N'جدید',50))V(Id,Grp,Value))
SELECT Grp,Value,CUME_DIST() OVER(PARTITION BY Grp ORDER BY Value) AS P FROM D ORDER BY Grp,Value;
گروهمقدارP
جدید501

نکته کاربردی: در گزارش، گروه تک‌عضوی را علامت‌گذاری کنید تا صفر یا یک به‌اشتباه برتری آماری تعبیر نشود.

مثال 7: مدیریت NULL

SQL Server در ترتیب صعودی NULL را در ابتدای مجموعه قرار می‌دهد؛ می‌توان آن را فیلتر یا با CASE مرتب کرد.

WITH D AS (SELECT * FROM (VALUES (1,CAST(NULL AS int)),(2,10),(3,20))V(Id,Value))
SELECT Id,Value,CUME_DIST() OVER(ORDER BY CASE WHEN Value IS NULL THEN 1 ELSE 0 END,Value) AS P FROM D ORDER BY Id;
IdمقدارP
1NULL1

نکته کاربردی: سیاست NULL باید صریح باشد؛ حذف، انتهای ترتیب یا یک گروه جدا سه معنای متفاوت دارند.

مثال 8: گزارش طبقه‌بندی مشتریان

خروجی نسبی می‌تواند برای برچسب سطح مشتری با CASE استفاده شود.

WITH Scores AS (SELECT * FROM (VALUES (1,N'الف',40),(2,N'ب',60),(3,N'پ',60),(4,N'ت',80),(5,N'ث',100))V(Id,Name,Score))
,W AS(SELECT *,CUME_DIST() OVER(ORDER BY Score) AS P FROM Scores) SELECT Name,Score,CASE WHEN P>=0.8 THEN N'طلایی' WHEN P>=0.4 THEN N'نقره‌ای' ELSE N'عادی' END AS Segment FROM W ORDER BY Score;
نامامتیازسطح
ث100طلایی

نکته کاربردی: حدود Segment را با تحلیل توزیع و ظرفیت خدمت‌رسانی تعیین کنید، نه با اعداد قراردادی بدون آزمون.

مثال 9: مقایسه روش نادرست تقسیم ROW_NUMBER

تقسیم ROW_NUMBER بر COUNT رفتار tie و نقطه شروع تابع استاندارد را بازتولید نمی‌کند.

WITH Scores AS (SELECT * FROM (VALUES (1,N'الف',40),(2,N'ب',60),(3,N'پ',60),(4,N'ت',80),(5,N'ث',100))V(Id,Name,Score))
SELECT Name,Score,1.0*ROW_NUMBER() OVER(ORDER BY Score)/COUNT(*) OVER() AS WrongFormula,CUME_DIST() OVER(ORDER BY Score) AS CorrectValue FROM Scores ORDER BY Score,Id;
روشردیف اول
تقسیم ROW_NUMBER0.20
CUME_DIST0.20

نکته کاربردی: از تابع داخلی استفاده کنید تا تعریف آماری و رفتار مقادیر مساوی دقیق بماند.

مثال 10: ایندکس مناسب رتبه‌بندی

ایندکس روی کلید گروه و مقدار مرتب‌سازی می‌تواند Sort را سبک‌تر کند.

CREATE TABLE #Scores(DeptId int,Score int,Id bigint);
INSERT #Scores VALUES(1,40,1),(1,60,2),(1,80,3),(2,50,4);
CREATE INDEX IX_Scores_Window ON #Scores(DeptId,Score,Id);
SELECT DeptId,Score,CUME_DIST() OVER(PARTITION BY DeptId ORDER BY Score) AS P FROM #Scores;
DROP TABLE #Scores;
شاخص کنترلهدف
Actual Planبررسی Sort و Spill

نکته کاربردی: نتیجه را با STATISTICS IO/TIME و طرح اجرای واقعی اندازه‌گیری کنید؛ وجود ایندکس به‌تنهایی تضمین استفاده نیست.

خطاهای رایج و روش اصلاح

  • برای مقادیر مساوی، خروجی یکسان و برابر انتهای گروه هم‌رتبه است؛ این رفتار را با ROW_NUMBER اشتباه نگیرید.
  • استفاده از ORDER BY غیرقطعی؛ یک کلید یکتا به انتهای ترتیب اضافه کنید.
  • اعمال WHERE در سطح اشتباه؛ ابتدا مشخص کنید فیلتر باید جمعیت پنجره را عوض کند یا فقط خروجی را محدود نماید.
  • تبدیل نوع ضمنی در عبارت یا مقدار پیش‌فرض؛ نوع‌ها را صریح و سازگار تعریف کنید.
  • فرض اینکه خروجی تابع همیشه deterministic است؛ مستندات و ترتیب داده را برای امکان تکرار نتیجه کنترل کنید.

ملاحظات کارایی و تحلیل Execution Plan

ستون‌های باریک و ایندکس مرتب‌شده از Spill در Sort جلوگیری می‌کنند؛ Grant حافظه را پایش کنید.

ابتدا با SET STATISTICS IO, TIME ON خط مبنا بگیرید و Actual Execution Plan را ذخیره کنید. وجود Sort بزرگ، Memory Grant بیش از نیاز یا هشدار Spill به tempdb نشانه‌ای است که ترتیب داده، برآورد Cardinality یا ظرفیت حافظه باید بررسی شود.

ایندکس پیشنهادی معمولاً با ستون‌های فیلتر برابری و PARTITION BY آغاز می‌شود، سپس ستون‌های ORDER BY و tie-breaker می‌آیند و ستون خروجی در INCLUDE قرار می‌گیرد. این یک نسخه عمومی است؛ ترتیب نهایی باید با Query واقعی، Selectivity و هزینه نگهداری DML سنجیده شود.

چند تابع پنجره‌ای با Window Specification یکسان را در یک SELECT بنویسید تا Optimizer امکان استفاده مشترک از ترتیب را داشته باشد. تفاوت کوچک در ترتیب صعودی، نزولی یا Frame می‌تواند عملگر جداگانه بسازد؛ پس طرح اجرا را پس از هر تغییر مقایسه کنید.

فیلتر دوره زمانی و ستون‌های غیرضروری را در جایی اعمال کنید که معنای تحلیل حفظ شود. کاهش عرض و تعداد ردیف ورودی، مصرف حافظه و I/O را کم می‌کند، ولی بهینه‌سازی نباید جمعیت آماری مورد نیاز را ناخواسته حذف کند.

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

  1. تعریف کسب‌وکار را پیش از نوشتن Query به پارتیشن، ترتیب و Frame تبدیل کنید.
  2. در ORDER BY از کلید یکتای پایدار برای شکستن tie استفاده کنید.
  3. NULL، پارتیشن تک‌ردیفی، مقادیر مساوی و مرزهای ابتدا و انتها را آزمون کنید.
  4. نوع داده خروجی و گرد کردن را مستند کنید و گرد کردن را تا لایه نمایش عقب بیندازید.
  5. Execution Plan، IO، CPU، Memory Grant و tempdb Spill را قبل و بعد از تغییر ثبت کنید.
  6. ایندکس را براساس بار کاری کامل طراحی کنید؛ بهبود SELECT نباید هزینه INSERT و UPDATE را نادیده بگیرد.

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

۱. تابع CUME_DIST دقیقاً چه مسئله‌ای را حل می‌کند؟

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

۲. اجزای OVER در CUME_DIST چه نقشی دارند؟

PARTITION BY مرز گروه منطقی را تعیین می‌کند و ORDER BY توالی تحلیل را می‌سازد. در توابع حساس به Frame، عبارت ROWS نیز دامنه ردیف‌های قابل مشاهده از هر ردیف را مشخص می‌کند.

۳. آیا استفاده از CUME_DIST هزینه توسعه گزارش را کم می‌کند؟

در بسیاری از گزارش‌ها حذف Self Join، Cursor یا کد میانی باعث Query کوتاه‌تر و نگهداری ساده‌تر می‌شود. برای برآورد تجاری باید حجم داده، SLA، دفعات اجرا و هزینه ایندکس نیز اندازه‌گیری شود.

۴. چه زمانی بازطراحی Queryهای قدیمی با CUME_DIST ارزش اقتصادی دارد؟

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

۵. تفاوت CUME_DIST با گزینه نزدیک آن چیست؟

PERCENT_RANK از رتبه شروع می‌کند؛ CUME_DIST تمام ردیف‌های هم‌رتبه را در صورت کسر می‌آورد. انتخاب نهایی باید براساس تعریف دقیق خروجی، رفتار tie، NULL و مرز پنجره انجام شود.

۶. برای پیاده‌سازی حرفه‌ای CUME_DIST در پروژه سازمانی چه خدمتی لازم است؟

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

۷. رایج‌ترین خطا در CUME_DIST چیست؟

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

۸. چگونه Performance تابع CUME_DIST را بسنجیم؟

ستون‌های باریک و ایندکس مرتب‌شده از Spill در Sort جلوگیری می‌کنند؛ Grant حافظه را پایش کنید. Actual Execution Plan، SET STATISTICS IO/TIME، Memory Grant، هشدار Spill و تعداد ردیف واقعی ابزارهای اصلی اندازه‌گیری هستند.

۹. بهترین روش نوشتن CUME_DIST چیست؟

ترتیب قطعی با tie-breaker یکتا، پارتیشن متناسب با منطق کسب‌وکار، تبدیل نوع صریح و آزمون NULL و مرزها را رعایت کنید. ابتدا صحت و سپس سرعت را بهینه سازید.

۱۰. CUME_DIST با کدام نسخه‌های SQL Server سازگار است؟

SQL Server 2012. در Azure SQL نیز اصل قابلیت در دسترس است، اما Compatibility Level و اصلاحات تجمعی مرتبط با IGNORE NULLS یا Optimizer باید بررسی شود.

سؤالات مصاحبه و پاسخ کوتاه

چرا CUME_DIST یک تابع پنجره‌ای است؟

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

چرا ORDER BY باید قطعی باشد؟

اگر چند ردیف کلید ترتیب یکسان داشته باشند، موتور می‌تواند ترتیب داخلی متفاوتی انتخاب کند. افزودن کلید یکتا نتیجه را تکرارپذیر و آزمون‌پذیر می‌سازد.

تفاوت WHERE داخلی و خارجی چیست؟

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

چه چیزی را در Execution Plan بررسی می‌کنید؟

Sort، Segment، Sequence Project یا Window Aggregate، Memory Grant، Spill به tempdb، برآورد ردیف و هم‌راستایی ایندکس با PARTITION و ORDER BY بررسی می‌شوند.

آزمون واحد مناسب چیست؟

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

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

  • نحو CUME_DIST و Compatibility Level کنترل شده است.
  • پارتیشن دقیقاً مرز موجودیت کسب‌وکار است.
  • ترتیب با tie-breaker یکتا قطعی شده است.
  • Frame در صورت اثرگذاری صریح نوشته شده است.
  • حالت NULL، tie و پارتیشن کوچک آزمون شده است.
  • فیلتر داخلی و خارجی آگاهانه انتخاب شده‌اند.
  • طرح اجرا و آمار IO/TIME ثبت شده‌اند.
  • لینک مقاله مادر و نمونه‌ها پیش از انتشار کنترل شده‌اند.

جمع‌بندی

تابع CUME_DIST وقتی ارزشمند است که برای محاسبه جایگاه تجمعی هر مقدار در توزیع و شناسایی صدک تقریبی به یک بیان مجموعه‌محور، خوانا و قابل بهینه‌سازی نیاز داشته باشیم. صحت ترتیب و پارتیشن مهم‌تر از کوتاهی ظاهری Query است و سنجش کارایی باید با داده و Plan واقعی انجام شود.

برای مقایسه CUME_DIST با هفت تابع دیگر، به مقاله مادر توابع Analytic و Window در SQL Server بازگردید.

 

0 نظر

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

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

حرف 500 حداکثر

اطلاعات تماس

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