سؤال

تحتوي العديد من التطبيقات على شبكات تعرض البيانات من جدول قاعدة البيانات صفحة واحدة في كل مرة.كما يتيح العديد منها أيضًا للمستخدم اختيار عدد السجلات في كل صفحة، والفرز حسب أي عمود، والتنقل ذهابًا وإيابًا عبر النتائج.

ما هي الخوارزمية الجيدة لتنفيذ هذا النمط دون إحضار الجدول بأكمله إلى العميل ثم تصفية البيانات الموجودة على العميل.كيف يمكنك إحضار السجلات التي تريد عرضها للمستخدم فقط؟

هل LINQ يبسط الحل؟

هل كانت مفيدة؟

المحلول

في MS SQL Server 2005 وما فوق، رقم الصف() يبدو أنه يعمل:

تي إس كيو إل:الترحيل باستخدام ROW_NUMBER()

DECLARE @PageNum AS INT;
DECLARE @PageSize AS INT;
SET @PageNum = 2;
SET @PageSize = 10;

WITH OrdersRN AS
(
    SELECT ROW_NUMBER() OVER(ORDER BY OrderDate, OrderID) AS RowNum
          ,OrderID
          ,OrderDate
          ,CustomerID
          ,EmployeeID
      FROM dbo.Orders
)

SELECT * 
  FROM OrdersRN
 WHERE RowNum BETWEEN (@PageNum - 1) * @PageSize + 1 
                  AND @PageNum * @PageSize
 ORDER BY OrderDate
         ,OrderID;

نصائح أخرى

أنصح إما باستخدام LINQ، أو محاولة نسخ ما يفعله.لدي تطبيق يمكنني من خلاله استخدام أساليب LINQ Take and Skip لاسترداد البيانات المقسمة إلى صفحات.يبدو الرمز كالتالي:

MyDataContext db = new MyDataContext();
var results = db.Products
    .Skip((pageNumber - 1) * pageSize)
    .Take(pageSize);

يكشف تشغيل SQL Server Profiler أن LINQ يقوم بتحويل هذا الاستعلام إلى SQL مشابه لما يلي:

SELECT [ProductId], [Name], [Cost], and so on...
FROM (
    SELECT [ProductId], [Name], [Cost], [ROW_NUMBER]
    FROM (
       SELECT ROW_NUMBER() OVER (ORDER BY [Name]) AS [ROW_NUMBER], 
           [ProductId], [Name], [Cost]
       FROM [Products]
    )
    WHERE [ROW_NUMBER] BETWEEN 10 AND 20
)
ORDER BY [ROW_NUMBER]

في سهل الانجليزية:
1.قم بتصفية صفوفك واستخدم الدالة ROW_NUMBER لإضافة أرقام الصفوف بالترتيب الذي تريده.
2.قم بالتصفية (1) لإرجاع أرقام الصفوف التي تريدها فقط في صفحتك.
3.قم بالفرز (2) حسب رقم الصف، وهو نفس الترتيب الذي تريده (حسب الاسم في هذه الحالة).

هناك طريقتان أساسيتان لإجراء ترقيم الصفحات في قاعدة البيانات (أفترض أنك تستخدم SQL Server):

باستخدام الأوفست

وقد شرح آخرون كيف ROW_NUMBER() OVER() يمكن استخدام وظيفة التصنيف لأداء الصفحات.ومن الجدير بالذكر أن SQL Server 2012 يتضمن أخيرًا دعمًا لمعيار SQL OFFSET .. FETCH بند:

SELECT first_name, last_name, score
FROM players
ORDER BY score DESC
OFFSET 40 ROWS FETCH NEXT 10 ROWS ONLY

إذا كنت تستخدم SQL Server 2012 ولا يمثل التوافق مع الإصدارات السابقة مشكلة، فمن الأفضل أن تفضل هذه الجملة لأنه سيتم تنفيذها بشكل أفضل بواسطة SQL Server في الحالات الزاوية.

استخدام أسلوب البحث

هناك طريقة مختلفة تمامًا وأسرع بكثير ولكنها أقل شهرة لتنفيذ الترحيل في SQL.يُطلق على هذا غالبًا اسم "طريقة البحث" كما هو موضح في هذا بلوق وظيفة هنا.

SELECT TOP 10 first_name, last_name, score
FROM players
WHERE (score < @previousScore)
   OR (score = @previousScore AND player_id < @previousPlayerId)
ORDER BY score DESC, player_id DESC

ال @previousScore و @previousPlayerId القيم هي القيم الخاصة بالسجل الأخير من الصفحة السابقة.يتيح لك ذلك جلب الصفحة "التالية".إذا ORDER BY الاتجاه هو ASC, ، استخدم ببساطة > بدلاً من.

باستخدام الطريقة المذكورة أعلاه، لا يمكنك الانتقال فورًا إلى الصفحة 4 دون جلب السجلات الأربعين السابقة أولاً.لكن في كثير من الأحيان، لا ترغب في القفز إلى هذا الحد على أي حال.بدلاً من ذلك، ستحصل على استعلام أسرع بكثير قد يكون قادرًا على جلب البيانات في وقت ثابت، اعتمادًا على الفهرسة الخاصة بك.بالإضافة إلى ذلك، تظل صفحاتك "مستقرة"، بغض النظر عما إذا كانت البيانات الأساسية قد تغيرت (على سبيل المثال.في الصفحة 1، بينما أنت في الصفحة 4).

هذه هي أفضل طريقة لتنفيذ الترحيل عند التحميل البطيء لمزيد من البيانات في تطبيقات الويب، على سبيل المثال.

لاحظ أن "طريقة البحث" تسمى أيضًا ترحيل مجموعة المفاتيح.

تم دمج LINQ مع تعبيرات lambda والفئات المجهولة في .Net 3.5 بشكل كبير يبسط هذا النوع من الأشياء.

الاستعلام عن قاعدة البيانات:

var customers = from c in db.customers
                join p in db.purchases on c.CustomerID equals p.CustomerID
                where p.purchases > 5
                select c;

عدد السجلات في الصفحة الواحدة:

customers = customers.Skip(pageNum * pageSize).Take(pageSize);

الفرز حسب أي عمود:

customers = customers.OrderBy(c => c.LastName);

الحصول على الحقول المحددة فقط من الخادم:

var customers = from c in db.customers
                join p in db.purchases on c.CustomerID equals p.CustomerID
                where p.purchases > 5
                select new
                {
                    CustomerID = c.CustomerID,
                    FirstName = c.FirstName,
                    LastName = c.LastName
                };

يؤدي هذا إلى إنشاء فئة مجهولة مكتوبة بشكل ثابت يمكنك من خلالها الوصول إلى خصائصها:

var firstCustomer = customer.First();
int id = firstCustomer.CustomerID;

يتم تحميل النتائج من الاستعلامات بشكل بطيء بشكل افتراضي، لذلك لا تتحدث إلى قاعدة البيانات حتى تحتاج إلى البيانات فعليًا.يعمل LINQ في .Net أيضًا على تبسيط التحديثات إلى حد كبير من خلال الاحتفاظ بسياق البيانات لأية تغييرات قمت بها، وتحديث الحقول التي قمت بتغييرها فقط.

حل أوراكل:

select * from (
    select a.*, rownum rnum from (
        YOUR_QUERY_GOES_HERE -- including the order by
    ) a
    where rownum <= MAX_ROW
 ) where rnum >= MIN_ROW

هناك بعض الحلول التي أستخدمها مع MS SQL 2005.

واحد منهم هو ROW_NUMBER().لكنني شخصيًا لا أحب ROW_NUMBER() لأنها لا تعمل على تحقيق نتائج كبيرة (قاعدة البيانات التي أعمل عليها كبيرة حقًا - أكثر من 1 تيرابايت من البيانات التي تدير آلاف الاستعلامات في الثانية - كما تعلم - شبكات اجتماعية كبيرة موقع).

وهنا الحل المفضل لدي.

سأستخدم نوعًا من التعليمات البرمجية الزائفة لـ T-SQL.

لنبحث عن الصفحة الثانية للمستخدمين مرتبة حسب الاسم الأول واللقب، حيث تحتوي كل صفحة على 10 سجلات.

@page = 2 -- input parameter
@size = 10 -- can be optional input parameter

if @page < 1 then begin
    @page = 1 -- check page number
end
@start = (@page-1) * @size + 1 -- @page starts at record no @start

-- find the beginning of page @page
SELECT TOP (@start)
    @forename = forename,
    @surname = surname
    @id = id
FROM
    users
ORDER BY
    forename,
    surname,
    id -- to keep correct order in case of have two John Smith.

-- select @size records starting from @start
SELECT TOP (@size)
    id,
    forename,
    surname
FROM
    users
WHERE
    (forename = @forename and surname = @surname and id >= @id) -- the same name and surname, but bigger id
    OR (forename = @forename and surname > @surname) -- the same name, but bigger surname, id doesn't matter
    OR (forename > @forename) -- bigger forename, the rest doesn't matter
ORDER BY
    forename,
    surname,
    id

في الواقع، لدى LINQ أساليب التخطي والأخذ التي يمكن دمجها لاختيار السجلات التي سيتم جلبها.

تحقق من تلك.

لقاعدة البيانات: ترقيم الصفحات في SQL Server 2005

هناك نقاش حول هذا هنا

تحصل هذه التقنية على رقم الصفحة 100000 من قاعدة بيانات مكونة من 150000 سطر في 78 مللي ثانية

باستخدام معرفة المحسن وSET ROWCOUNT، يتم تخزين معرف الموظف الأول في الصفحة المطلوبة في متغير محلي لنقطة بداية.بعد ذلك، قم بتعيين ROWCOUNT على الحد الأقصى لعدد السجلات المطلوبة في @maximumRows.وهذا يسمح بترحيل مجموعة النتائج بطريقة أكثر كفاءة.يؤدي استخدام هذه الطريقة أيضًا إلى الاستفادة من الفهارس الموجودة مسبقًا في الجدول حيث أنها تنتقل مباشرةً إلى الجدول الأساسي وليس إلى جدول تم إنشاؤه محليًا.

أخشى أنني لا أستطيع الحكم على ما إذا كانت أفضل من الإجابة المقبولة حاليًا.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top