پیاده سازی عملیات صفحه بندی (paging) در sql server
اندازه‌ی قلم متن
تخمین مدت زمان مطالعه‌ی مطلب: سه دقیقه

در خیلی مواقع ملاحظه میشود که برای نمایش تعدادی از رکوردهای یک جدول در پایگاه داده، کل مقادیر موجود درآن توسط یک دستور select به دست می‌آید و صفحه‌بندی خروجی، به کنترلهای موجود سپرده میشود. اگر پایگاه داده ما دارای تعداد زیادی رکورد باشد، آن موقع است که دچار مشکل می‌شویم. فرض کنید به طور همزمان ۵ نفر (که تعداد زیادی نیستند) از برنامه ما که شامل ۱۰۰۰۰۰ سطر داده میباشد استفاده کنند و در هر صفحه، ۱۰ رکورد نمایش داده شود و صفحه‌بندی ما از نوع معقولی نباشد. در این صورت به جای اینکه با ۵×۱۰ رکورد داده را بارگزاری کنیم، ۵×۱۰۰۰۰۰ رکورد یعنی ۵۰۰۰۰۰ رکورد را برای به دست آوردن ۵۰ رکورد بارگزاری میکنیم. در زیر روشی شرح داده میشود که توسط آن، این سربار اضافه از روی برنامه و سرورهای مربوطه حذف شود. به stored procedure و توضیحات مربوط به آن توجه فرمایید :

CREATE PROCEDURE sp_PagedItems
(
 @Page int,
 @RecsPerPage int
)
AS

-- We don't want to return the # of rows inserted
-- into our temporary table, so turn NOCOUNT ON
SET NOCOUNT ON


--Create a temporary table
CREATE TABLE #TempItems
(
ID int IDENTITY,
Name varchar(50),
Price currency
)


-- Insert the rows from tblItems into the temp. table
INSERT INTO #TempItems (Name, Price)
SELECT Name,Price FROM tblItem ORDER BY Price

-- Find out the first and last record we want
DECLARE @FirstRec int, @LastRec int
SELECT @FirstRec = (@Page - 1) * @RecsPerPage
SELECT @LastRec = (@Page * @RecsPerPage + 1)

-- Now, return the set of paged records, plus, an indiciation of we
-- have more records or not!
SELECT *,
MoreRecords =
(
 SELECT COUNT(*)
 FROM #TempItems TI
 WHERE TI.ID >= @LastRec
)
FROM #TempItems
WHERE ID > @FirstRec AND ID < @LastRec


-- Turn NOCOUNT back OFF
SET NOCOUNT OFF
در این کد دو پارامتر از نوع integer تعریف میکنیم. اول پارامتر @Page که مربوط به شماره صفحه‌ای می‌باشد که قصد دارید آن‌را بارگزاری نمایید. دومین پارامتر با نام @RecsPerPage تعداد رکوردهایی است که هر بار میخواهید بارگزاری شوند. مثلا اگر میخواهید هر بار ۱۵ عدد از رکوردها را نمایش دهید، این مقدار را باید برابر ۱۵ قرار دهیم. در مرحله بعد یک جدول موقت با نام #TempItems ساخته شده است که به طور موقت مقادیری را در حافظه نگه میدارد. نکته کلیدی که جلوتر از آن استفاده شده، ستون با نام ID است که از نوع auto-increment بوده و روی جدول موقت تعریف شده است. این ستون شناسه هر سطر را در خود نگه میدارد که به صورت اتوماتیک بالا میرود و جزء لاینفکی از این نوع paging میباشد. پس از آن جدول موقت را توسط رکوردهای جدول واقعی با نام tblItem توسط دستور select پر میکنیم.

در مرحله بعد شماره اولین و آخرین سطر مورد نظر را بر اساس پارامترهای ورودی محاسبه کرده و در متغیرهای @FirstRec و @LastRec می‌ریزیم.
برای استفاده از این کد فقط کافیست که پارامترهای ورودی را مقداردهی نمایید. مثلا اگر میخواهید در یک کنترل Grid از آن استفاده کنید باید ابتدا یک کوئری داشته باشید که تعداد کل سطرها را به شما بدهد و بر اساس این مقدار تعداد صفحات مورد نظر را به دست آورید. پس از آن با کلیک روی هر کدام از شماره صفحات آن را به عنوان مقدار به پارامتر مورد نظر بفرستید و از آن لذت ببرید. 

  • #
    ‫۹ سال و ۱۱ ماه قبل، سه‌شنبه ۲۲ مهر ۱۳۹۳، ساعت ۲۳:۲۹
    ضمن تشکر از شما. یک اصلاح کوچک: جدول موقتی ایجاد شده در پایان کار رویه ذخیره شده باید drop بشه.
    • #
      ‫۹ سال و ۱۱ ماه قبل، سه‌شنبه ۲۲ مهر ۱۳۹۳، ساعت ۲۳:۴۸
      از دقت شما به این نکته ظریف ممنونم
      این موضوع در راستای آموزش عنوان مطلبش بود اما نکته شما جهت بالا بردن کیفیت و بهینه کردن کد مورد استفاده قرار میگیرد و عدم drop مشکلی در رسیدن به هدف مورد نظر ایجاد نمی‌کند
      متشکرم
  • #
    ‫۹ سال و ۱۱ ماه قبل، چهارشنبه ۲۳ مهر ۱۳۹۳، ساعت ۰۰:۵۰
    در sql 2012 به بعد جهت صفحه بندی دستورات offset و fetch اضافه شده که از لحاظ Performance بهینه‌تر از باقی روش‌های میباشد . مقایسه صفحه بندی‌های مختلف
    • #
      ‫۹ سال و ۱۱ ماه قبل، چهارشنبه ۲۳ مهر ۱۳۹۳، ساعت ۰۱:۲۴
      ممنونم بخاطر لینک مفیدی که در قسمت نظر ارسال نمودید
      تنها نقطه ضعف این مقاله همینطور که خود شما هم متذکر شده اید این است که برای ورژن‌های بانک اطلاعاتی بعد از 2012 قابل استفاده است. هنوز بسیاری از نرم افزارها و سازمانهای ما هنوز با ورژن‌های قدیمی‌تر کار می‌کنند.

      متشکرم
  • #
    ‫۹ سال و ۱۱ ماه قبل، چهارشنبه ۲۳ مهر ۱۳۹۳، ساعت ۱۴:۵۹
    ممنون از مطلب خوبتون
    به نظرم اگه جوری Sp را مینوشتید که یک ورودی متنی Query یا یک جدول موقت میگرفت و عمل Paging را روی اون انجام میداد مطلبتون بسیار کامل‌تر بود. مثلا ورودی Sp به این صورت بود که (1,10,'select * from Tbl_1') بازهم ممنون
    • #
      ‫۹ سال و ۱۱ ماه قبل، چهارشنبه ۲۳ مهر ۱۳۹۳، ساعت ۱۶:۲۴
      سلام
      ممنونم از نظرتون
      دوتا پارامتر داره از ورودی دریافت می‌کنه و هدف نحوه انجام صفحه بندی بوده

      متشکرم
  • #
    ‫۹ سال و ۱۱ ماه قبل، چهارشنبه ۲۳ مهر ۱۳۹۳، ساعت ۱۷:۰۶
    روشی بسیار قدیمی‌است و این روزها آنچنان کاربرد ندارد.
    برای صفحه‌بندی :
    - در SQL Server 2008 از ROW_NUMBER استفاده می‌کنند.
    - در SQL Server 2012 به بعد از OFFSET FETCH استفاده می‌کنند