使用函数优化 SQL 查询
Posted
技术标签:
【中文标题】使用函数优化 SQL 查询【英文标题】:Optimizing SQL query using function 【发布时间】:2014-08-14 12:26:52 【问题描述】:我有一个运行速度非常慢的存储过程。我运行了 SP 并查看了执行计划,并且能够看到花费了这么长时间。
慢的部分:
DECLARE
@id int
,@date datetime
,@endDate datetime
SELECT @id = 3483
,@date = DATEADD(DAY, -10, GETDATE())
,@endDate = GETDATE()
SET NOCOUNT ON
SELECT *
,prevId = dbo.fnGetPrevId(id)
FROM dbo.table WITH(READUNCOMMITTED)
这个查询中比较慢的部分是我调用函数 dbo.fnGetPrevId 的地方。
dbo.fnGetPrevId:
DECLARE @prevId int
SELECT TOP 1 @prevId = t2.id
FROM dbo.table2 AS t2 WITH(READUNCOMMITTED)
RETURN @prevId
是否可以在不创建索引或类似的东西的情况下重写以获得更好的性能?
【问题讨论】:
是的,但是我该怎么做呢?做不到 = nPrevResultsId = (SELECT TOP 1...) ? 【参考方案1】:您可以使用子查询代替标量值函数。
// ...
,prevId = (
SELECT TOP 1 x.id
FROM dbo.table AS x WITH(READUNCOMMITTED)
WHERE 1 = 1)
// ...
在大多数情况下,最好避免引用表的标量值函数,因为它们基本上是每行需要运行一次的黑盒,并且无法通过查询计划引擎进行优化。
【讨论】:
会尝试这个解决方案 非常感谢。那效果好多了。怎么这么快? @krillezzz:如前所述,该函数是优化器的黑匣子,引擎可能不使用索引或其他优化。这是一个相关的问题:Why is a UDF so much slower than a subquery?【参考方案2】:首先,您应该将函数全部剪切并内联查询。从我看来,这将是相当简单的。或者,如果您想保留一个函数,请使用表值函数。对于这两项检查:
http://technet.microsoft.com/en-us/library/ms175156(v=sql.105).aspx
其次,构建索引将获得最佳优化结果(巨大改进)
【讨论】:
索引不是我的选择【参考方案3】:您的 UDF 正在针对它运行的每一行重新编译。要停止这种情况,请将函数设为表值内联函数,如下所示:
create function dbo.fnFunction:
( list of parameters here)
Returns Table
As
Return
(
SELECT TOP 1 id
FROM dbo.table WITH(READUNCOMMITTED)
WHERE id= @id
AND id2= @id2
...
ORDER BY date DESC
)
这消除了对每一行的重新编译,因为内联表值函数中的 SQL 包含在使用它的查询的 sql 中,并存储在整个查询一次且仅一次的缓存计划中。要使用结果,您需要像对待表格一样对待结果。只需在您的外部查询中加入它,检查此link
【讨论】:
【参考方案4】:首先在 table(id, id2, id3....) 上创建索引。
这可能会解决您的问题。否则,试试cross apply
:
FROM dbo.table1 AS x WITH(READUNCOMMITTED) cross apply
(SELECT TOP 1 x2.id
FROM dbo.table2 as x2 WITH(READUNCOMMITTED)
WHERE x.id= x2.id
AND x.id2= x2.id2
AND x.id3= x2.id3
ORDER BY x.Date DESC
) x
【讨论】:
以上是关于使用函数优化 SQL 查询的主要内容,如果未能解决你的问题,请参考以下文章