استعلام متصفح باستخدام الفرز على أعمدة مختلفة باستخدام row_number () Over () في SQL Server 2005
-
04-07-2019 - |
سؤال
لنفترض أنني أستخدم قاعدة بيانات NorthWind وأرغب في تشغيل استعلام عبر إجراء مخزن يحتوي ، من بين معلمات أخرى ، على ما يلي:
@Offset
للإشارة إلى مكان بدء ترقيم الصفحات ،@Limit
للإشارة إلى حجم الصفحة ،@SortColumn
للإشارة إلى العمود المستخدم لأغراض الفرز ،@SortDirection
, ، للإشارة إلى فرز الصعود أو النسل.
تتمثل الفكرة في القيام بالترقيم في قاعدة البيانات ، حيث تحتوي مجموعة النتائج على آلاف الصفوف ، لذا فإن التخزين المؤقت ليس خيارًا (ولا يعتبر استخدام ViewState ، IMO ، تمتص).
كما تعلمون ، يوفر SQL Server 2005 الوظيفة رقم الصف أيّ إرجاع العدد المتسلسل للصف داخل قسم من مجموعة النتائج ، بدءًا من 1 للصف الأول في كل قسم.
نحتاج إلى الفرز على كل عمود تم إرجاعه (خمسة في هذا المثال) و Dynamic SQL ليس خيارًا ، لذلك لدينا احتمالان: استخدام الكثير من IF ... ELSE ...
والحصول على 10 استفسارات ، وهو جحيم للحفاظ عليه ، أو الحصول على استعلام مثل ما يلي:
WITH PaginatedOrders AS (
SELECT
CASE (@SortColumn + ':' + @SortDirection)
WHEN 'OrderID:A' THEN ROW_NUMBER() OVER (ORDER BY Orders.OrderID ASC)
WHEN 'OrderID:D' THEN ROW_NUMBER() OVER (ORDER BY Orders.OrderID DESC)
WHEN 'CustomerID:A' THEN ROW_NUMBER() OVER (ORDER BY Orders.CustomerID ASC)
WHEN 'CustomerID:D' THEN ROW_NUMBER() OVER (ORDER BY Orders.CustomerID DESC)
WHEN 'EmployeeID:A' THEN ROW_NUMBER() OVER (ORDER BY Orders.EmployeeID ASC)
WHEN 'EmployeeID:D' THEN ROW_NUMBER() OVER (ORDER BY Orders.EmployeeID DESC)
WHEN 'OrderDate:A' THEN ROW_NUMBER() OVER (ORDER BY Orders.OrderDate ASC)
WHEN 'OrderDate:D' THEN ROW_NUMBER() OVER (ORDER BY Orders.OrderDate DESC)
WHEN 'ShippedDate:A' THEN ROW_NUMBER() OVER (ORDER BY Orders.OrderID ASC)
WHEN 'ShippedDate:D' THEN ROW_NUMBER() OVER (ORDER BY Orders.OrderID DESC)
END AS RowNumber,
OrderID, CustomerID, EmployeeID, OrderDate, ShippedDate
FROM Orders
-- WHERE clause goes here
)
SELECT
RowNumber, OrderID, CustomerID, EmployeeID, OrderDate, ShippedDate,
@Offset, @Limit, @SortColumn, @SortDirection
FROM PaginatedOrders
WHERE RowNumber BETWEEN @Offset AND (@Offset + @Limit - 1)
ORDER BY RowNumber
لقد جربت الاستعلام عدة مرات ، مع حجج مختلفة ، وأدائه جيد جدًا في الواقع ، ولكن يبدو أنه قد يتم تحسينه بطريقة أخرى.
هل أي شيء خاطئ في هذا الاستعلام أم أنك ستفعل ذلك بهذه الطريقة؟ هل تقترح نهجًا مختلفًا؟
المحلول
بسيط:
SELECT
OrderID, CustomerID, EmployeeID, OrderDate, ShippedDate,
@Offset, @Limit, @SortColumn, @SortDirection
FROM
Orders
WHERE
ROW_NUMBER() OVER
(
ORDER BY
/* same expression as in the ORDER BY of the whole query */
) BETWEEN (@PageNum - 1) * @PageSize + 1 AND @PageNum * @PageSize
/* AND more conditions ... */
ORDER BY
CASE WHEN @SortDirection = 'A' THEN
CASE @SortColumn
WHEN 'OrderID' THEN OrderID
WHEN 'CustomerID' THEN CustomerID
/* more... */
END
END,
CASE WHEN @SortDirection = 'D' THEN
CASE @SortColumn
WHEN 'OrderID' THEN OrderID
WHEN 'CustomerID' THEN CustomerID
/* more... */
END
END DESC
سيتم فرز هذا على NULL (DESC) إذا تم تحديد ترتيب ASC ، أو العكس.
دع وظيفة row_number () تعمل على نفس الترتيب عن طريق التعبير.