使用非重复和row_number分页时的性能

时间:2018-08-15 05:32:25

标签: sql sql-server database-performance query-performance

我有一个类似这样的SQL:

SELECT A,B,C,FUN(A) AS A FROM someTable

问题是FUN()是一个非常慢的函数,因此,如果someTable中有很多记录,则会出现很大的性能问题。

如果我们使用分页,则可以解决此问题,我们可以这样进行分页:

SELECT * FROM(
SELECT A,B,C,FUN(A), Row_number()OVER( ORDER BY B ASC) AS rownum FROM someTable
)T WHERE T.rownum >=1 AND T.rownum<20

在此脚本中,FUN()仅执行20次,因此性能正常。 但是我们需要使用别名进行排序,因此我们无法内联编写rownum,必须移至子查询或CTE,我们选择了CTE,它看起来像这样:

;WITH CTE AS (
   SELECT A,B AS alias,C,FUN(A) FROM someTable
)
SELECT * FROM(
SELECT *,Row_number()OVER( ORDER BY alias ASC) AS rownum FROM CTE
)T WHERE T.rownum >=1 AND T.rownum<20

到目前为止,我们一切顺利,我们可以通过分页来解决性能问题,也可以解决别名顺序问题,但是无论如何我们都需要在查询中添加DISTINCT

 ;WITH CTE AS (
       SELECT DISTINCT A,B AS alias,C,FUN(A) FROM someTable
    )
    SELECT * FROM(
    SELECT *,Row_number()OVER( ORDER BY alias ASC) AS rownum FROM CTE
    )T WHERE T.rownum >=1 AND T.rownum<20

此后,此SQL的优化似乎消失了,FUN()的执行次数将是someTable中记录数的很多倍,我们再次遇到性能问题。

基本上我们被封锁在这里,有什么建议吗?

1 个答案:

答案 0 :(得分:3)

问题在于,为了获得不同的值,数据库引擎必须在所有选中的记录上运行fun(a)函数。

如果仅在最终选择中执行fun(a),则distinct不会对其产生影响,因此它仅应在最后20条记录上运行。

我已将您曾经使用过的派生表更改为另一个cte(但这是个人喜好-在我看来,不将派生表和cte混合使用更整洁):

;WITH CTE1 AS (
    SELECT DISTINCT A,B AS alias,C
    FROM someTable
), 
CTE2 AS
(
    SELECT *, ROW_NUMBER() OVER(ORDER BY alias) As RowNum
    FROM CTE1
)

SELECT *, FUN(A)
FROM CTE2
WHERE RowNum >= 1 
AND RowNum < 20

请注意,由于fun函数不是确定性的,因此您获得的结果可能与原始查询有所不同-因此在采用此解决方案之前,请先比较结果。