使用SQL Server ROW_NUMBER()分页性能不佳

时间:2013-08-05 15:43:32

标签: sql performance tsql sql-server-2008-r2

我正在尝试建立一个分页机制。我正在使用创建SQL的ORM,如下所示:

SELECT * FROM 
    (SELECT t1.colX, t2.colY
         ROW_NUMBER() OVER (ORDER BY t1.col3) AS row 
     FROM Table1 t1
     INNER JOIN Table2 t2
     ON t1.col1=t2.col2
    )a 
WHERE row >= n AND row <= m

表1具有> 500k行,而表2具有> 10k记录

我直接在SQL Server 2008 R2 Management Studio中执行查询。子查询需要2-3秒才能执行,但整个查询需要> 2分钟。

我知道SQL Server 2012接受OFFSET .. LIMIT ..选项,但我无法升级软件。

任何人都可以帮助我提高查询的性能或建议可以通过ORM软件施加的其他分页机制。

Update:

测试Roman Pekar的解决方案(请参阅解决方案的评论)证明ROW_NUMBER()可能不是导致性能问题的原因。不幸的是问题仍然存在。

由于

3 个答案:

答案 0 :(得分:3)

我从评论中了解你的表结构。

create table Table2
(
  col2 int identity primary key,
  colY int
)

create table Table1
(
  col3 int identity primary key,
  col1 int not null references Table2(col2),
  colX int
)

这意味着从Table1返回的行永远不会被Table2的联接过滤,因为Table1.col1not null。由于Table2是主键,因此Table2.Col2的联接也不能向结果中添加行。

然后,您可以在加入Table1之前重写查询以在Table2生成行号。在联接到Table2之前,也会应用where子句,这意味着您只能在Table2中找到实际属于结果集的行。

select T1.colX,
       T2.colY,
       T1.row
from
    (
    select col1,
           colX,
           row_number() over(order by col3) as row
    from Table1
    ) as T1
  inner join Table2 as T2
    on T1.col1 = T2.col2
where row >= @n and row <= @m

SQL Fiddle

我不知道你是否可以使用你的ORM(Lightspeed by Mindscape)生成这样的分页查询,而不是你现在拥有的。

此答案的查询计划: enter image description here

使用问题中的查询的查询计划: enter image description here

两者之间的读取存在巨大差异。

enter image description here

答案 1 :(得分:1)

仅将分页表的主键列插入到具有标识列的临时表中,按排序列排序。 (您可能必须包含ordered-by列以确保排序正确。)然后,使用临时表作为所需行的键连接回主表。如果数据是相当静态的,您可以将订购数据保存到会话密钥的永久表而不是临时表,并在短时间内重复使用(因此几分钟内的后续页面请求几乎是即时的)。 / p>

Row_Number()往往在小数据集中表现良好,但是一旦你得到一些严重的行,就会遇到严重的性能障碍,就像你有500k一样。

答案 2 :(得分:0)

我建议您检查表格上的索引。如果您至少在col2 table2上有索引,我认为这将有助于您的查询。您也可以尝试重写您的查询,如

;with cte1 as (
    select top (@m) t1.colX, t2.colY, t1.col3
    from Table1 as t1
        inner join Table2 as t2 on t1.col1=t2.col2
    order by t1.col3 asc
),
cte2 as (
    select top (@m - @n + 1) *
    from cte1
    order by col3 desc
)
select *
from cte2 as t1

但如果您没有索引,它仍然可能会很慢