کاربرد ROLLUP در SQL Server: جایگزینی قدرتمند برای COMPUTE BY در گزارشهای تحلیلی
بند COMPUTE BY در نسخههای قبلی SQL Server بسیار مفید بود، اما اکنون منسوخ شده است. این بند برای افزودن ردیفهای خلاصه به مجموعههای نتیجه، مانند مجموع کل، تعداد و میانگین استفاده میشد. با وجود منسوخ شدن، همچنان میتوانید آن را در SQL Server 2008 و نسخههای بعدی برای سازگاری عقبرو استفاده کنید، اما در نسخه آتی SQL Server حذف خواهد شد. در صورت استفاده از آن با بندهای دیگر مانند TOP، OFFSET، FOR XML، FOR JSON و INTO، با پیام خطا مواجه خواهید شد.
برای نشان دادن عملکرد COMPUTE BY، یک مثال ساده از پایگاه داده AdventureWorks2014 را در نظر بگیرید. این کوئری، اطلاعات مربوط به سفارشات محصول را بر اساس TerritoryID مرتبسازی میکند و سپس مجموع LineTotal را برای هر TerritoryID محاسبه میکند.
USE [AdventureWorks2014]
GO
SELECT TerritoryID,
ProductID,
OrderQty,
UnitPrice,
LineTotal
FROM Sales.SalesOrderDetail
WHERE ProductID IN (799, 798, 771)
ORDER BY TerritoryID
COMPUTE SUM(LineTotal) BY TerritoryID;
همانطور که مشاهده میکنید، نتیجه کوئری شامل یک ردیف اضافی برای هر TerritoryID با مجموع LineTotal است. حال بیایید سعی کنیم نتایج مشابهی را با استفاده از بند ROLLUP به دست آوریم. ROLLUP یک توسعه برای بند GROUP BY است که به ما امکان میدهد ردیفهای اضافی را شامل شویم که عملیات خلاصهسازی را برای هر گروه نمایش میدهند. این بند برای تولید گزارشهایی که شامل زیرمجموعهها (subtotals) و مجموع کل (grand totals) هستند، بسیار مفید است.
کوئری ROLLUP زیر نتایج مشابهی را برمیگرداند. با افزودن WITH ROLLUP به بند GROUP BY، میتوانیم مجموع کل برای هر TerritoryID و همچنین یک مجموع کل نهایی برای تمام ردیفها را در یک کوئری واحد تولید کنیم. این روش برای تجزیه و تحلیل دادهها و گزارشگیری بسیار کارآمد است.
USE [AdventureWorks2014]
GO
SELECT TerritoryID,
SUM(LineTotal) AS Total
FROM Sales.SalesOrderDetail
WHERE ProductID IN (799, 798, 771)
GROUP BY TerritoryID WITH ROLLUP
ORDER BY TerritoryID;
اگر از ROLLUP با چندین ستون در بند GROUP BY استفاده کنیم، ردیفهای خلاصهسازی را برای هر ترکیب از ستونها تولید خواهد کرد. بیایید مثالی را بررسی کنیم.
در این مثال، ROLLUP نه تنها مجموع کل OrderQty را برای هر TerritoryID نمایش میدهد، بلکه زیرمجموعههایی را نیز برای هر ProductID در داخل هر TerritoryID ایجاد میکند. این قابلیت برای گزارشهایی که نیاز به جزئیات چندسطحی دارند، بسیار مفید است.
USE [AdventureWorks2014]
GO
SELECT TerritoryID,
ProductID,
SUM(OrderQty) AS TotalOrderQty
FROM Sales.SalesOrderDetail
WHERE ProductID IN (799, 798, 771)
GROUP BY TerritoryID, ProductID WITH ROLLUP
ORDER BY TerritoryID, ProductID;
هنگام استفاده از ROLLUP، اگر مقادیر NULL را در خروجی مشاهده کردید که انتظار یک مقدار گروهبندی شده را دارید، به این معنی است که آن ردیف یک ردیف خلاصهسازی است. میتوانید مقادیر NULL را با استفاده از توابع ISNULL یا COALESCE جایگزین کنید.
برای بهبود خوانایی و وضوح گزارش، میتوانیم مقادیر NULL تولید شده توسط ROLLUP را با عبارات توصیفی جایگزین کنیم. در این مثال، ISNULL برای جایگزینی NULL در TerritoryID با “Grand Total” و در ProductID با “Sub Total” استفاده شده است. این کار کمک میکند تا ردیفهای مجموع کل و زیرمجموعه به راحتی قابل تشخیص باشند.
USE [AdventureWorks2014]
GO
SELECT ISNULL(CAST(TerritoryID AS VARCHAR(10)), 'Grand Total') AS TerritoryID,
ISNULL(CAST(ProductID AS VARCHAR(10)), 'Sub Total') AS ProductID,
SUM(OrderQty) AS TotalOrderQty
FROM Sales.SalesOrderDetail
WHERE ProductID IN (799, 798, 771)
GROUP BY TerritoryID, ProductID WITH ROLLUP
ORDER BY TerritoryID, ProductID;
فرض کنید میخواهیم تعداد کل سفارشات را بر اساس TerritoryID به دست آوریم و همچنین یک زیرمجموعه برای هر ProductID در داخل هر TerritoryID داشته باشیم. علاوه بر این، میخواهیم مجموع کل نهایی را برای تمام سفارشات نیز به دست آوریم.
این کوئری نهایی، تمام قابلیتهای پیشین را ترکیب میکند. با استفاده از ROLLUP بر روی TerritoryID و ProductID، نه تنها تعداد کل سفارشات (TotalOrderQty) و مجموع خط (TotalLineTotal) را برای هر ترکیب فراهم میکند، بلکه زیرمجموعههای دقیق و یک مجموع کل نهایی جامع را نیز ارائه میدهد. این مثال قدرت ROLLUP را در گزارشگیری پیچیده و تحلیل داده به وضوح نشان میدهد.
USE [AdventureWorks2014]
GO
SELECT ISNULL(CAST(TerritoryID AS VARCHAR(10)), 'Grand Total') AS TerritoryID,
ISNULL(CAST(ProductID AS VARCHAR(10)), 'Sub Total') AS ProductID,
SUM(OrderQty) AS TotalOrderQty,
SUM(LineTotal) AS TotalLineTotal
FROM Sales.SalesOrderDetail
WHERE ProductID IN (799, 798, 771)
GROUP BY TerritoryID, ProductID WITH ROLLUP
ORDER BY TerritoryID, ProductID;