我有一个类似这样的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
中记录数的很多倍,我们再次遇到性能问题。
基本上我们被封锁在这里,有什么建议吗?
答案 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
函数不是确定性的,因此您获得的结果可能与原始查询有所不同-因此在采用此解决方案之前,请先比较结果。