我意识到这是一个常见的问题,之前已在SO 上进行了讨论,但我想我会再次提出这个问题,希望找到一些可行的替代方案.
采用以下SQL,它将分页与动态排序相结合:
WITH CTE AS (
SELECT
OrderID,
ROW_NUMBER() OVER (ORDER BY
CASE WHEN @SortCol='OrderID' THEN OrderID END ASC,
CASE WHEN @SortCol='CustomerName' THEN Surname END ASC
) AS ROW_ID
FROM Orders WHERE X
)
SELECT Orders.* FROM CTE
INNER JOIN Orders ON CTE.OrderID = Orders.OrderID
WHERE ROW_ID BETWEEN @RowStart AND @RowStart + @RowCount -1;
Run Code Online (Sandbox Code Playgroud)
众所周知,ROW_NUMBER()方法在大型表上不能很好地工作,因为在ORDER BY子句中使用多个CASE语句时,无法正确使用表上的索引(请参阅链接).
我们多年来一直使用的解决方案是构造一个字符串,然后使用sp_executesql执行该字符串.使用这样的动态SQL时性能很好,但从易读性的角度来看,生成的代码很糟糕.
我听说过ROWCOUNT方法,但据我所知,当你按元素引入动态顺序时,它仍然容易受到同样的问题的影响.
那么,冒着问不可能的风险,还有其他选择吗?
编辑
为了在这里取得一些有用的进展,我已经汇总了三个查询,突出了各种建议的方法:
当前的动态SQL解决方案(执行时间147ms)
gbn解决方案(执行时间1687ms)
Anders解决方案(执行时间1604ms)
Muhmud解决方案(执行时间46ms)
这个怎么样:
WITH data as (
SELECT OrderID,
ROW_NUMBER() OVER ( ORDER BY OrderID asc) as OrderID_ROW_ID,
ROW_NUMBER() OVER ( ORDER BY Surname asc) as Surname_ROW_ID
FROM Orders --WHERE X
), CTE AS (
SELECT OrderID, OrderID_ROW_ID as ROW_ID
FROM data
where @SortCol = 'OrderID'
union all
SELECT OrderID, Surname_ROW_ID
FROM data
where @SortCol = 'Surname'
)
SELECT Orders.*, ROW_ID FROM CTE
INNER JOIN Orders ON CTE.OrderID = Orders.OrderID
WHERE ROW_ID BETWEEN @RowStart AND @RowStart + @RowCount -1
order by ROW_ID
option (recompile);
Run Code Online (Sandbox Code Playgroud)
编辑:使用option (recompile)对在岗的例子查询使得去快得多。但是,case不能完全以这种方式在升序/降序之间进行选择。
这样做的原因是正在为不合适的变量值生成计划,然后缓存该计划。强制重新编译允许它使用变量的实际值。
尝试这个。这应该利用您拥有的索引
WITH CTE AS (
SELECT
Orders.*,
ROW_NUMBER() OVER (ORDER BY OrderID) AS rnOrderID,
ROW_NUMBER() OVER (ORDER BY Surname) AS rnSurname
FROM Orders WHERE X
)
SELECT CTE.*
FROM CTE
WHERE
CASE @SortCol
WHEN 'OrderID' THEN rnOrderID
END BETWEEN @RowStart AND @RowStart + @RowCount -1;
Run Code Online (Sandbox Code Playgroud)
但是,对于大型数据集(100,000 或更多),还有其他技术,例如https://web.archive.org/web/20211020131201/https://www.4guysfromrolla.com/webtech/042606-1.shtml