Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/27.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
y将执行(如果您有兴趣了解更多这方面的信息,请在最后介绍一些历史) 第三,如果您碰巧在t-sql标量函数中放入子查询或“查找”,则可能会实现代码分解,但会完全蒙蔽优化器,因为优化器无法看到可能加快查询速度的连接顺序。因此,虽然在过程语言中,尝试像这样考虑常见的编码模式是完全有意义的,但当优化器需要重写所有内容以获得尽可能快的查询计划时,这没有什么意义_Sql_Sql Server_Query Performance - Fatal编程技术网

y将执行(如果您有兴趣了解更多这方面的信息,请在最后介绍一些历史) 第三,如果您碰巧在t-sql标量函数中放入子查询或“查找”,则可能会实现代码分解,但会完全蒙蔽优化器,因为优化器无法看到可能加快查询速度的连接顺序。因此,虽然在过程语言中,尝试像这样考虑常见的编码模式是完全有意义的,但当优化器需要重写所有内容以获得尽可能快的查询计划时,这没有什么意义

y将执行(如果您有兴趣了解更多这方面的信息,请在最后介绍一些历史) 第三,如果您碰巧在t-sql标量函数中放入子查询或“查找”,则可能会实现代码分解,但会完全蒙蔽优化器,因为优化器无法看到可能加快查询速度的连接顺序。因此,虽然在过程语言中,尝试像这样考虑常见的编码模式是完全有意义的,但当优化器需要重写所有内容以获得尽可能快的查询计划时,这没有什么意义,sql,sql-server,query-performance,Sql,Sql Server,Query Performance,一般来说,过去10多年来针对SQL Server的大多数指南都建议不要使用标量T-SQL函数,原因我已经解释过了。您将发现的大多数外部内容都可能与此概念一致。请注意,从历史上看,SQL Server确实内联了单语句T-SQL表值函数(将它们视为SQL中的视图),但这是一个完整的历史工件,显然与T-SQL标量函数的处理方式不一致 微软的QP团队早就知道这些了。然而,修复这些问题需要大量的工作才能使系统进入一种形式,在这种形式下,标量T-SQL函数内联通常会帮助所有客户,而不会导致某些查询变慢。不幸

一般来说,过去10多年来针对SQL Server的大多数指南都建议不要使用标量T-SQL函数,原因我已经解释过了。您将发现的大多数外部内容都可能与此概念一致。请注意,从历史上看,SQL Server确实内联了单语句T-SQL表值函数(将它们视为SQL中的视图),但这是一个完整的历史工件,显然与T-SQL标量函数的处理方式不一致

微软的QP团队早就知道这些了。然而,修复这些问题需要大量的工作才能使系统进入一种形式,在这种形式下,标量T-SQL函数内联通常会帮助所有客户,而不会导致某些查询变慢。不幸的是,大多数商业优化器的工作方式创建了一个模型,该模型根据计算工作方式的某些假设来估计运行时。该模型将是不完整的(例如:正如我所指出的,我们今天根本不需要t-sql标量函数)。拥有一个模型的一个不明显的副作用是,一些查询将位于模型之外(这意味着优化器正在猜测或使用不完整的数据),并得到一个很好的计划。一些查询将在模型之外,并得到一个可怕的计划。模型内的查询并不总是能得到一个很好的计划,但平均来说它们做得更好。更进一步说,如果成本或考虑的备选方案集从一个主要的SQL版本更改到下一个版本,那么当您升级时,您可能会得到一个与以前不同的计划。对于那些“模型之外”的情况,效果是相当随机的——在某些情况下,你可以得到更快或更慢的计划。因此,如果没有一套机制来找到防止计划回归的方法,就很难改变优化器的成本模型,否则许多客户将有一些查询被“调优”为一组不完整的假设,然后当这些假设从他们下面改变时,会得到一个更糟糕的计划。Net:optimizer团队没有改变成本模型来解决这个问题,因为在升级时有足够的机制提供良好的客户体验之前,平均而言,这会对客户造成比处理痛苦更大的伤害

在过去的几个版本中,这正是SQL团队一直在做的事情。首先,对所考虑的成本模型或计划集(称为搜索空间)的任何更改都被更改为与数据库的兼容性级别相关联。这允许客户升级,保持旧的兼容性级别,因此通常不会在同一硬件上看到计划更改。它还允许客户尝试更换新的,并在工作量出现问题时立即停机,从而大大降低了以前单向升级的风险。您可以阅读有关升级建议的更多信息。其次,SQL团队添加了一个“飞行数据记录器”,用于随着时间的推移进行计划选择,称为查询存储。它捕获以前的计划以及这些计划的执行情况。这使客户可以在更快的情况下“返回”到以前的计划(即使您受到其中一个脱模案例的影响)。这为防止在升级时破坏应用程序提供了另一个级别的保险

(抱歉,这是长篇大论——背景很重要)

对于SQL Server 2019+SQL Azure,QP团队引入了一种机制,该机制内联了许多T-SQL标量函数。你可以看公告。在这个特性上仍然有一些试探法正在被调整,以确保与不内联相比,性能回归很少/没有(这意味着QP通常会确定何时内联比不内联更好,并且只内联那些情况)。当内联时,优化器具有重新排序联接的能力,并考虑各种计划选择方案,以获得更快的查询计划。因此,最终,这将在查询处理器中使用普通的关系运算符,并以这种方式降低成本


我希望这可以解释为什么您现在的速度可能较慢,并让您对SQL团队确实正在努力改进我们所说的场景抱有一些希望。祝您在调整应用程序时好运。

T-SQL中的标量函数历来对查询性能有害的原因有两个(尽管这有望很快改善-我将在最后解释)

  • 首先,即使T-SQL标量函数只包含标量逻辑(没有查询或联接),在查询中间调用解释函数也会有开销。对于具有多行且T-SQL标量处理速度较慢的查询,此开销是可以测量的。我不确定在所有情况下,它都会使您的查询速度减慢几倍,但还有其他原因可能会使查询速度减慢
  • 其次,在查询优化器中处理标量操作的方式可能会以您意想不到的方式对性能产生负面影响。SQL查询优化开始时使用的一种启发式方法是将标量操作向下推到查询的叶子上。这是为了允许优化器匹配计算列(可以持久化,因此可以加快昂贵的标量计算)。然而,这样做的负面后果是,执行非持久化计算的频率可能比您预期的要高。因此,如果您有一个从查询b输出1000行的筛选器/联接
    FUNCTION funcOne(@colA, @colB, @colC, @valX, @valY)
    RETURNS INT AS
    BEGIN
        RETURN CASE
            WHEN @colA = @colB
                THEN @valX + @valY
            WHEN @colC BETWEEN 1 AND 10
                THEN @valX
            ELSE 0
        END
    END
    
    FUNCTION funcTwo(@colA, @colB, @colC, @valX, @valY)
    RETURNS INT AS
    BEGIN
        RETURN CASE
            WHEN @colA <> @colB
                THEN @valX + @valY
            WHEN @colC BETWEEN 1 AND 10
                THEN @valY
            ELSE 0
        END
    END
    
    SELECT mt.[Ident]
        ,SUM(funcOne(mt.[colA], ot.[colB], ot.[colC], mt.[valX], ot.[valY])) AS funcOne
        ,SUM(funcTwo(mt.[colA], ot.[colB], ot.[colC], mt.[valX], ot.[valY])) AS funcTwo
    FROM MyTable AS mt
    INNER JOIN SomeOtherTable AS ot
        ON mt.[Ident] = ot.[Ident]
    WHERE mt.[colA] BETWEEN 1 AND 100
    GROUP BY mt.[Ident]
    
    FUNCTION funcOne(@colA, @colB, @colC, @valX, @valY)
    RETURNS TABLE AS
    RETURN (
        SELECT CASE
            WHEN @colA = @colB
                THEN @valX + @valY
            WHEN @colC BETWEEN 1 AND 10
                THEN @valX
            ELSE 0
        END AS [MyValue]
    )
    
    FUNCTION funcTwo(@colA, @colB, @colC, @valX, @valY)
    RETURNS TABLE AS
    RETURN (
        SELECT CASE
            WHEN @colA <> @colB
                THEN @valX + @valY
            WHEN @colC BETWEEN 1 AND 10
                THEN @valY
            ELSE 0
        END AS [MyValue]
    )
    
    SELECT mt.[Ident]
        ,SUM(funcOne.[MyValue]) AS funcOneValue
        ,SUM(funcTwo.[MyValue]) AS funcTwoValue
    FROM MyTable AS mt
    INNER JOIN SomeOtherTable AS ot
        ON mt.[Ident] = ot.[Ident]
    CROSS APPLY funcOne(mt.[colA], ot.[colB], ot.[colC], mt.[valX], ot.[valY]) AS funcOne
    CROSS APPLY funcTwo(mt.[colA], ot.[colB], ot.[colC], mt.[valX], ot.[valY]) AS funcTwo
    WHERE mt.[colA] BETWEEN 1 AND 100
    GROUP BY mt.[Ident]
    
    alter table mytable add funcone as ( your `case` expression here );
    
    CREATE FUNCTION fn_GetName(@CustomerID int)
    RETURNS varchar(100)
    AS 
    RETURN (
      DECLARE @myResult VARCHAR(100);
      SELECT @myResult =  Fullname
      FROM Sales s
      WHERE s.CustomerID = @CustomerID
      RETURN @myResult 
     )
    
    select 
       fn_GetName(id)
    from Student;
    
    CREATE FUNCTION fn_GetNameTable(@CustomerID int)
    RETURNS TABLE
    AS 
    RETURN (  
      SELECT Fullname
      FROM Sales s
      WHERE s.CustomerID = @CustomerID
     )
    
    SELECT I.[Customer Name]
      ,S.CustomerType
    FROM Sales s
    CROSS APPLY fn_GetNameTable(S.CustomerID) I