我会尝试类似的事情:
WITH CTEOrder AS (
SELECT
OrderID,
ROW_NUMBER() OVER (ORDER BY OrderID ASC) AS ROW_ID
FROM Orders
)
, CTECustomerName AS (
SELECT
OrderID,
ROW_NUMBER() OVER (ORDER BY Surname ASC) AS ROW_ID
FROM Orders
)
, CTECombined AS
(
SELECT 'OrderID' OrderByType, OrderID, Row_ID
FROM CTEOrder
WHERE Row_id BETWEEN @RowStart AND @RowStart + @RowCount -1
UNION
SELECT 'CustomerName' OrderByType, OrderID, Row_ID
FROM CTECustomerName
WHERE row_id BETWEEN @RowStart AND @RowStart + @RowCount -1
)
SELECT Orders.* FROM CTECombined
INNER JOIN Orders ON CTECombined.OrderID = Orders.OrderID
WHERE ROW_ID BETWEEN @RowStart AND @RowStart + @RowCount -1;
AND OrderByType = @SortCol
我用我自己的一张桌子尝试了这个,它有应用程序。400 万条记录。显然,它有不同的字段名称,所以如果我没有在答案中正确“翻译”这个并且 SQL 没有为您运行,我深表歉意。但是,这个想法应该是显而易见的。
使用您问题中的代码,我在我的表上获得了应用程序 200000 逻辑读取和 6068 毫秒 CPU,而上面我获得了 1422 逻辑读取和 78 毫秒 CPU。
我没有刷新缓存或任何其他真正的基准测试所需的东西,但我确实尝试了不同的页面、页面大小等,我的结果从一开始就一致。
如果您有许多想要排序的不同字段的查询,则此解决方案可能无法充分扩展,因为您必须扩展 CTE 的数量,但如果您仍然在构建 SQL,则可以在代码中执行此操作 - 并且对于对两个不同字段进行排序的示例对我来说就像一个魅力。
编辑:想一想,您可能不需要为每个 OrderBy 列单独的 CTE,您可能只需要一个在同一个 CTE 中同时执行ROW_NUMBER()
的UNION
CTE。原理是一样的,我认为优化器最终会做同样的事情,但我还没有进行基准测试。如果有时间验证,我会更新答案。
编辑 2:正如预期的那样,您可以UNION
在一个 CTE 内完成。我不会更新代码,但我会按原样提供一些代码基准。我做了 10000 行的页面大小,看看这是否有很大的不同,但没有。(cusinar9 和我的代码的两次运行分别是等效运行的,所以冷启动两个版本的代码参数相同,第二次运行的参数不同,但两个版本的代码相同):
cusimar9的代码,冷启动:
Table 'TestTable'. Scan count 10009, logical reads 43080, physical reads 189, read-ahead reads 12915, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times: CPU time = 3037 ms, elapsed time = 2206 ms.
cusimar9 的代码,第二次运行,不同的参数:
Table 'TestTable'. Scan count 10009, logical reads 43096, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times: CPU time = 4132 ms, elapsed time = 1012 ms.
我的建议,冷启动:
Table 'TestTable'. Scan count 10001, logical reads 31963, physical reads 12, read-ahead reads 6984, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times: CPU time = 218 ms, elapsed time = 1410 ms.
我的建议,第二次运行:
Table 'TestTable'. Scan count 10001, logical reads 31963, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times: CPU time = 218 ms, elapsed time = 358 ms.
编辑3:看到您发布的代码,我注意到了这个结构:
PagedCTE AS (
SELECT (SELECT Max(ROW_ID) FROM OrderByCTE) AS TOTAL_ROWS, OrderID
FROM OrderByCTE
WHERE
OrderByCTE.SortCol = @SortCol AND OrderByCTE.SortDir = @SortDir AND
OrderByCTE.ROW_ID BETWEEN @RowStart AND @RowStart + @RowCount -1
)
我不能 100% 确定这样做的目的,但我猜您想返回总行数,以便您可以计算(并向用户显示)我们在记录集中的距离。那么为什么不把这个数字排除在所有ORDER BY
噪音之外呢?要坚持当前的风格,为什么不使用 ? 制作 CTE SELECT COUNT(*)
?然后加入你的最终选择?