استعلام متصفح باستخدام الفرز على أعمدة مختلفة باستخدام row_number () Over () في SQL Server 2005

StackOverflow https://stackoverflow.com/questions/230058

  •  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 () تعمل على نفس الترتيب عن طريق التعبير.

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