Sar*_*tha 4 performance sql-server offset-fetch paging query-performance
目前,我有一个base_voter
包含大约 100M 虚拟数据的数据表。我有如下存储过程:
CREATE Procedure [dbo].[spTestingBaseVoter]
@SortColumn NVARCHAR(128) = N'name_voter',
@SortDirection VARCHAR(4) = 'asc',
@offset INT,
@limit INT
As
Begin
SET NOCOUNT ON;
-- reject any invalid sort directions:
IF LOWER(@SortDirection) NOT IN ('asc','desc')
BEGIN
RAISERROR('Invalid parameter for @SortDirection: %s', 11, 1, @SortDirection);
RETURN -1;
END
-- reject any unexpected column names:
IF LOWER(@SortColumn) NOT IN (N'name_voter', N'home_street_address_1', N'home_address_city')
BEGIN
RAISERROR('Invalid parameter for @SortColumn: %s', 11, 1, @SortColumn);
RETURN -1;
END
--SET @SortColumn = QUOTENAME(@SortColumn);
DECLARE @sql NVARCHAR(MAX);
SET @sql = N'SELECT id, name_voter, home_street_address_1, home_address_city
FROM dbo.base_voter
WITH(NOLOCK)
WHERE deleted_at IS NULL'
SET @sql = @sql + N' ORDER BY ' + @SortColumn + ' ' + @SortDirection +
' OFFSET @OF ROWS
FETCH NEXT @LIM ROWS ONLY ';
EXEC sp_executesql @sql,
N'@OF int,@LIM int',
@OF=@offset, @LIM=@limit
End
Run Code Online (Sandbox Code Playgroud)
为了使查询更快,我还创建了索引:
CREATE NONCLUSTERED INDEX FIBaseVoterWithDeletedAt
ON dbo.base_voter (name_voter asc,home_street_address_1, home_address_city)
WHERE deleted_at IS NULL ;
Run Code Online (Sandbox Code Playgroud)
通过创建这个非聚集索引,我大大减少了查询时间。但是,它不适用于具有较高偏移量的查询。
例如:
Execute spTestingBaseVoter name_voter,asc,9999950,50
Run Code Online (Sandbox Code Playgroud)
是不是我做错了什么,导致了这个性能问题?或者,更有必要按降序创建另一个索引。
让我知道是否有更好的方法来解决这种情况,这可能会大大减少查询时间。
更新:
预计执行计划
小智 5
根据Dan Guzman最初留下的评论回答:
OFFSET
不是魔法;时间会随着偏移量的增加而逐渐变慢。此外,您应该为每个要排序的列设置一个单独的索引,但 SQL Server 可以向前或向后读取每个列,因此您不需要额外的降序排列。
您可以使用键分页代替行号分页,传递最后检索到的有序列和主键的值。然后查询可以指定`SELECT TOP(n) WHERE '。这将允许用户向前滚动(并以类似的逻辑向后滚动)。如果您必须进行行号分页,请在缓存层中进行。
请参阅优化服务器端分页 - 第 I 部分和Itzik Ben-Gan 的T-SQL 查询:TOP 和 OFFSET-FETCH(示例章节)。
需要一个锚过滤器来避免逐渐变大的扫描,这对于像你这样的大分页结果返回一个页面很远的结果是很昂贵的。我已经将这种方法用于具有亚秒响应时间的数十亿行表,尽管那是在 SQL 2000 天,我认为用户从未滚动到最后。