【问题标题】:SQL Server (T-SQL): Avoid call scalar function multiple timesSQL Server (T-SQL):避免多次调用标量函数
【发布时间】:2017-11-08 19:41:26
【问题描述】:

我有一个存储过程,它执行SELECT 来返回一些行。在SELECT 中,我需要检查一个条件以便为某些列返回正确的值。这个条件包括一个标量函数。对于正在处理的行,所有时间标量函数都使用相同的参数调用,见下文:

SELECT
    Id,
    Name,
    Surname,
    CASE WHEN (dbo.GetNumTravels(Id) >= 50)
        THEN 10
        ELSE 99
    END as Factor1,
    CASE WHEN (dbo.GetNumTravels(Id) >= 50)
        THEN 15
        ELSE -1
    END as Factor2,
    CASE WHEN (dbo.GetNumTravels(Id) >= 50)
        THEN 30
        ELSE 70
    END as Factor3
FROM 
    Employees
WHERE 
    DepartmentId = 100

我担心性能,我的意思是,我不喜欢多次调用标量函数dbo.GetNumTravels,那么如何避免这种情况,只调用一次,然后在我需要的时候一直使用它?

【问题讨论】:

  • 你为什么首先要求这个? GetNumTravels 做了什么,为什么它很慢?如果您想查找每个员工的旅行,您应该在 WHERE 子句中使用联接或子查询,而不是在 SELECT 子句中使用单独的查询。您可以使用 view 代替函数并将其与 Employees 连接

标签: sql-server-2008 tsql user-defined-functions


【解决方案1】:

标量用户定义函数因性能不佳而臭名昭著。如果您可以将其转换为内联表值函数,您可以期望看到性能提升。

如果您将标量函数转换为内联表值函数,您可以使用 cross apply() 为每一行调用一次,如下所示:

select
     Id,
     Name,
     Surname,
     case when x.NumTravels >= 50
        then 10
        else 99
     end as Factor1,
     case when x.NumTravels >= 50)
        then 15
        else -1
     end as Factor2,
     case when x.NumTravels >= 50
        then 30
        else 70
     end as Factor3
from Employees
  cross apply dbo.GetNumTravels_itvf(e.Id) x
where DepartmentId = 100

参考:

【讨论】:

    【解决方案2】:

    您可以通过使用派生表的概念来实现这一点,在派生表中,我们曾经只调用一次函数dbo.GetNumTravels(Id) 并在外部查询中使用它的输出,这可能有助于通过避免多次调用同一函数来在一定程度上获得性能.

    SELECT 
           Id,
           Name,
           Surname,
           CASE WHEN (NumTravelsID >= 50)  THEN 10  ELSE 99 END as Factor1,
           CASE WHEN (NumTravelsID >= 50)  THEN 15  ELSE -1 END as Factor2,
           CASE WHEN (NumTravelsID >= 50)  THEN 30  ELSE 70 END as Factor3
    FROM (
             SELECT
                Id,
                Name,
                Surname,
                dbo.GetNumTravels(Id) as NumTravelsID
             FROM Employees
             WHERE DepartmentId = 100  
     )M
    

    【讨论】:

      【解决方案3】:

      我不确定性能(无论如何,你的测试也要考虑其他答案),但我想测试一下。我也尝试减少函数的使用和 CASE 的使用。请告诉我

      SELECT A.*
          , 10*F0+99*~F0 AS FACTOR1
          , 15*F0-1*~F0 AS FACTOR2
          , 30*F0+70*~F0 AS FACTOR3
      FROM (
          SELECT
           Id,
           Name,
           Surname,
          CAST(CASE WHEN (dbo.GetNumTravels(Id) >= 50) THEN  1  ELSE 0 END AS BIT) AS F0     
          FROM Employees
          WHERE DepartmentId = 100
           ) A
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-02-22
        • 1970-01-01
        相关资源
        最近更新 更多