【问题标题】:SQL IF ELSE performance issueSQL IF ELSE 性能问题
【发布时间】:2016-08-16 23:05:01
【问题描述】:

我有一个存储过程,它可以从 2 个不同的来源获取数据,具体取决于用户是从单个关闭期间(存档到数据仓库表中)还是从开放期间(来自事务表的数据)请求数据。

如果我将限制选择的参数传递给数据仓库表(为关闭期间提供年份和期间),则该过程需要很长时间才能返回结果,除非我注释掉 ELSE BEGIN... 代码。没有数据来自代码的 ELSE 部分,但它仍在减慢该过程。如果我注释掉代码的 ELSE 部分,它会非常快。

我已经尝试过OPTION (RECOMPILE),并且我正在使用局部变量来避免参数嗅探,但这并没有帮助。有没有办法解决这个问题? 以下是我正在执行的运行缓慢的示例:

IF @Year <> 0 AND @Period <> 0 AND (SELECT PerClosedTimestamp
                                    FROM Period
                                    WHERE 
                                        PerCompanyID = @CompanyID AND
                                        PerYear = @Year AND
                                        PerPeriod = @Period) IS NOT NULL
BEGIN
    SELECT   
        datawhse.column1, datawhse.column2, etc …
    FROM    
        datawhse        
END 
ELSE
BEGIN
    SELECT 
        trantable.column1, trantable.column2, etc…
    FROM    
        trantable       
END 

如果我排除 ELSE 语句,它会运行得非常快:

IF @Year <> 0 
   AND @Period <> 0 
   AND (SELECT PerClosedTimestamp
        FROM Period
        WHERE PerCompanyID = @CompanyID 
          AND PerYear = @Year   
          AND PerPeriod = @Period) IS NOT NULL
BEGIN
    SELECT   datawhse.column1
            ,datawhse.column2, etc …
    FROM    datawhse        
END 

【问题讨论】:

  • 不是一个答案,而是一个有趣的相关(不重复)帖子dba.stackexchange.com/questions/9835/…
  • 如果在“PerClosedTimestamp”前面添加TOP 1 或使用EXISTS 会怎样。还尝试将条件更改为 IF Year = 0 或 Period = 0 或 (select...) 为空,然后执行第二个块,否则执行第一个块。尝试将“WHERE Year = 0 or Period = 0”(通过复制条件)放入第二个块。
  • 很高兴看到慢速和快速执行的查询计划。
  • 寻求性能帮助的问题,应添加表架构、执行计划、涉及的表数和重现的最少步骤..
  • 您可以将 2 个选择拆分为存储过程;那么每个人都会得到一个更适合的查询计划。 (刚刚意识到这是一个老问题!)

标签: sql-server performance if-statement stored-procedures


【解决方案1】:

@Year 和@Period 是否直接来自存储过程的输入?就像在你的 sproc 定义中一样,你是这样写的吗?

create proc USP_name @Year int, @Period int as
begin
     ...
end

你可以尝试使用局部变量,根据我在很多情况下的经验,局部变量有很大帮助。

create proc USP_name @Year int, @Period int as
begin
    declare @Year_local int, @Period_local int
    set @Year_local = @Year, @Period_local = @period

    if @Year_local <> 0 AND @Period_local <> 0 AND ...
    ....
end

【讨论】:

  • 感谢大家的建议。我最终创建了 2 个单独的函数来从数据仓库表或事务表返回数据。我从 IF THEN ELSE 语句中的函数中进行选择,这似乎解决了我的问题。
  • 这成功了!将查询放入字符串并使用 sp_executesql 也很快:但由于潜在的 SQL 注入而没有使用。
【解决方案2】:

正如 cmets 中所提到的,为什么慢的明确答案总是可以在查询计划中找到。

据推测,trantable 在过程中的出现会使查询优化器以一种不喜欢datawhse 的方式产生偏差。我很想至少尝试UNION ALL 而不是IF/THEN,类似于

SELECT   
    datawhse.column1, datawhse.column2, etc …
FROM    
    datawhse
WHERE @Year <> 0 AND @Period <> 0 AND (SELECT PerClosedTimestamp
                                FROM Period
                                WHERE 
                                    PerCompanyID = @CompanyID AND
                                    PerYear = @Year AND
                                    PerPeriod = @Period) IS NOT NULL
UNION ALL
SELECT 
    trantable.column1, trantable.column2, etc…
FROM    
    trantable       
WHERE @Year = 0 OR @Period = 0 OR (SELECT PerClosedTimestamp
                                FROM Period
                                WHERE 
                                    PerCompanyID = @CompanyID AND
                                    PerYear = @Year AND
                                    PerPeriod = @Period) IS NULL

看看查询计划如何比较会很有趣。

【讨论】:

    【解决方案3】:

    感谢大家的建议。我最终创建了 2 个单独的函数来从数据仓库表或事务表返回数据。我从 IF THEN ELSE 语句中的函数中进行选择,这似乎解决了我的问题。

    【讨论】:

      猜你喜欢
      • 2022-12-07
      • 1970-01-01
      • 2012-01-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-14
      • 2021-05-15
      相关资源
      最近更新 更多