使用 distinct 和 row_number 分页时的性能
Posted
技术标签:
【中文标题】使用 distinct 和 row_number 分页时的性能【英文标题】:Performance when using distinct and row_number pagination 【发布时间】:2018-08-15 05:32:24 【问题描述】:我有一个类似这样的 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
中的记录数,我们再次遇到性能问题。
基本上我们都卡在这里了,有什么建议吗?
【问题讨论】:
FUN()
是什么?
FUN
是确定性的吗? (意味着每次获得相同的输入时它会返回相同的值)
@Zohar Peled,不,它是动态的,实际上它可能是一个子查询,所以很难为它缓存完整的结果或地图,我们试过了。
【参考方案1】:
问题在于,为了获得不同的值,数据库引擎必须对所有被选中的记录运行fun(a)
函数。
如果您只在最终选择中执行fun(a)
,distinct
应该不会影响它,因此它应该只在最后 20 条记录上运行。
我已将您使用的派生表更改为另一个 cte(但这是个人喜好 - 在我看来,不要将派生表和 ctes 混合使用更整洁):
;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
函数不是确定性的,您可能会得到与原始查询不同的结果 - 因此在调整此解决方案之前先比较结果。
【讨论】:
是的,你的笔记是我关心的,这样FUN(A)
字段不会被DISTINCT
考虑,所以结果会不一样。
好吧,也许更好的办法是提高FUN
的性能 - 但这应该是另一个问题的主题。
我认为你是对的,只要数据库必须对所有记录执行该功能,我们就无能为力。我会尝试将函数字段替换为其他一些字段组合以达到相同的目的,然后在分页后运行函数,感谢您的帮助!
很高兴为您提供帮助 :-)以上是关于使用 distinct 和 row_number 分页时的性能的主要内容,如果未能解决你的问题,请参考以下文章
详述 SQL 中的 distinct 和 row_number() over() 的区别及用法