【问题标题】:Very Slow SQL Server Query using Functions使用函数的非常慢的 SQL Server 查询
【发布时间】:2012-11-20 14:15:58
【问题描述】:

这是上一个问题的延续:

SQL Server Query time out depending on Where Clause

根据 where 子句,查询运行速度会非常缓慢。我使用 CTE 重写了该查询并很好地避免了该问题,但从未找到答案。

最近对另一个类似的查询进行了调整以添加另一个字段,但它的性能突然从运行并返回 10,000 行的大约 30 秒下降到需要 10 多个小时(最终返回相同的结果集)。今天我开始解决这个问题,发现了一些奇怪的东西。

我经常需要从日期时间值中仅提取日期部分,因此我将逻辑写入了一个函数:

CREATE FUNCTION [dbo].[cDate] ( @pInputDate    DATETIME )
RETURNS DATETIME
BEGIN
        RETURN CAST(CONVERT(VARCHAR(10), @pInputDate, 111) AS DATETIME)
END
GO

我在这个新的、低效的查询中发现,如果我用查询中的 CAST(CONVERT(VARCHAR(10), @pInputDate, 111) AS DATETIME) 内联替换该函数,查询执行速度会从大约 10 小时下降到不到 2 秒。我看不出估计的执行计划有什么不同。顺便说一句,这不是添加的字段。我假设添加另一个字段会以某种方式导致执行计划发生变化并放大上述情况。

我的问题是,这正常吗?如上所述,我将函数用于重复过程,因为如果你找到一种更有效的做事方式,它们更容易维护、记忆和更新。我应该对我的函数做些什么来提高它们的性能吗?

【问题讨论】:

  • 是的,众所周知。搜索标量值函数。这是 RBAR...
  • 您使用的是什么版本的 SQL Server?也许CAST(@pInputDate AS DATE) 会更有效率(这将适用于 2008 或更新版本)...
  • 不幸的是,我现在被困在 2005 年,所以这不是一个选择。
  • 谢谢米奇。看起来我有一些重新设计要做...

标签: sql sql-server performance tsql sql-function


【解决方案1】:

如果您必须将其封装在一个函数中,请参阅Scalar functions, inlining, and performance: An entertaining title for a boring post

改写如下

CREATE FUNCTION dbo.cDate_Inline
(
    @pInputDate DATETIME
)
RETURNS TABLE
AS
    RETURN
    (
        SELECT DATEADD(day, DATEDIFF(Day, 0, @pInputDate), 0) AS [Date]
    )

然后代替

SELECT *,
       [dbo].[cDate](modify_date) AS modified
FROM   sys.objects 

使用

SELECT *,
       ca.Date AS modified
FROM   sys.objects
       CROSS APPLY dbo.cDate_Inline(modify_date) ca 

这将被查询优化器内联到计划中。

【讨论】:

  • 救命稻草,谢谢!为我节省了大量的重构 :)
【解决方案2】:

我会尝试添加另一个变量来进行声明并返回。对此的思考过程可能是您的演员和转换以更改您输入的日期时间可能是瓶颈。情况可能并非如此,但通常如果您在范围内设置一个新变量,进行调节并将其绑定到该变量并返回该变量,这可能有助于提高速度。我倾向于尽可能远离标量函数,但当您开始将它们用于更大的数据集时,它们会出现很多性能问题。只是一个建议,它可能会或可能不会有帮助,但它会将返回隔离到独立于输入的新对象:

CREATE FUNCTION [dbo].[cDate] ( @pInputDate    DATETIME )
RETURNS DATETIME
BEGIN
Declare @Output datetime  = CAST(CONVERT(VARCHAR(10), @pInputDate, 111) AS DATETIME)  -- 2008 method and newer

Declare @Output datetime;

Select @Output  = CAST(CONVERT(VARCHAR(10), @pInputDate, 111) AS DATETIME)  -- 2005 and prior method 


return @Output
END
GO

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-02
    • 2015-11-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多