减少查询时间以获得更高的 sql server 偏移量

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 天,我认为用户从未滚动到最后。