sql >> データベース >  >> RDS >> Sqlserver

セットベースのプランは、多くの条件でスカラー値関数よりも実行が遅くなります

    ここでのキーワード用語はINLINE テーブル値関数 。 T-SQLのテーブル化された値関数には、マルチステートメントとインラインの2種類があります。 T-SQL関数がBEGINステートメントで始まる場合、それはがらくたになります-スカラーまたはそれ以外の場合。一時テーブルをインラインに入れることはできません テーブル値関数なので、スカラーからマルチステートメントのテーブル値関数に移行したと思いますが、これはおそらくもっと悪いでしょう。

    インラインテーブル値関数(iTVF)は次のようになります。

    CREATE FUNCTION [dbo].[Compute_value]
    (
      @alpha FLOAT,
      @bravo FLOAT,
      @charle FLOAT,
      @delta FLOAT
    )
    RETURNS TABLE WITH SCHEMABINDING AS RETURN
    SELECT newValue = 
      CASE WHEN @alpha IS NULL OR @alpha = 0 OR @delta IS NULL OR @delta = 0 THEN 0
           WHEN @bravo IS NULL OR @bravo <= 0 THEN 100
           ELSE @alpha * POWER((100 / @delta), 
                 (-2 * POWER(@charle * @bravo, DATEDIFF(<unit of measurement>,GETDATE(),'1/1/2000')/365)))
      END
    GO;
    

    投稿したコードでは、DATEDIFF ステートメントにdatepartがありません パラメータ。次のようになります:

    @x int = DATEDIFF(DAY, GETDATE(),'1/1/2000')   
    

    もう少し進んでください-iTVFがT-SQLスカラー値のユーザー定義関数よりも優れている理由を理解することが重要です。これは、テーブル値関数がスカラー値関数よりも高速であるためではなく、MicrosoftによるT-SQLインライン関数の実装がインラインでないT-SQL関数の実装よりも高速であるためです。同じことを行う次の3つの関数に注意してください。

    -- Scalar version
    CREATE FUNCTION dbo.Compute_value_scalar
    (
      @alpha FLOAT,
      @bravo FLOAT,
      @charle FLOAT,
      @delta FLOAT
    )
    RETURNS FLOAT
    AS
    BEGIN
        IF @alpha IS NULL OR @alpha = 0 OR @delta IS NULL OR @delta = 0 
        RETURN 0
    
        IF @bravo IS NULL OR @bravo <= 0
            RETURN 100
    
        IF (@charle + @delta) / @bravo <= 0
            RETURN 100
        DECLARE @x int = DATEDIFF(dd, GETDATE(),'1/1/2000')     
        RETURN @alpha * POWER((100 / @delta), (-2 * POWER(@charle * @bravo, @x/365)))
    END
    GO
    
    -- multi-statement table valued function 
    CREATE FUNCTION dbo.Compute_value_mtvf
    (
      @alpha FLOAT,
      @bravo FLOAT,
      @charle FLOAT,
      @delta FLOAT
    )
    RETURNS  @sometable TABLE (newValue float) AS 
        BEGIN
        INSERT @sometable VALUES
    (
      CASE WHEN @alpha IS NULL OR @alpha = 0 OR @delta IS NULL OR @delta = 0 THEN 0
           WHEN @bravo IS NULL OR @bravo <= 0 THEN 100
           ELSE @alpha * POWER((100 / @delta), 
                 (-2 * POWER(@charle * @bravo, DATEDIFF(DAY,GETDATE(),'1/1/2000')/365)))
      END
    )
    RETURN;
    END
    GO
    
    -- INLINE table valued function
    CREATE FUNCTION dbo.Compute_value_itvf
    (
      @alpha FLOAT,
      @bravo FLOAT,
      @charle FLOAT,
      @delta FLOAT
    )
    RETURNS TABLE WITH SCHEMABINDING AS RETURN
    SELECT newValue = 
      CASE WHEN @alpha IS NULL OR @alpha = 0 OR @delta IS NULL OR @delta = 0 THEN 0
           WHEN @bravo IS NULL OR @bravo <= 0 THEN 100
           ELSE @alpha * POWER((100 / @delta), 
                 (-2 * POWER(@charle * @bravo, DATEDIFF(DAY,GETDATE(),'1/1/2000')/365)))
      END
    GO
    

    次に、いくつかのサンプルデータとパフォーマンステストについて説明します。

    SET NOCOUNT ON;
    CREATE TABLE #someTable (alpha FLOAT, bravo FLOAT, charle FLOAT, delta FLOAT);
    INSERT #someTable
    SELECT TOP (100000)
      abs(checksum(newid())%10)+1, abs(checksum(newid())%10)+1, 
      abs(checksum(newid())%10)+1, abs(checksum(newid())%10)+1
    FROM sys.all_columns a, sys.all_columns b;
    
    PRINT char(10)+char(13)+'scalar'+char(10)+char(13)+replicate('-',60);
    GO
    DECLARE @st datetime = getdate(), @z float;
    
    SELECT @z = dbo.Compute_value_scalar(t.alpha, t.bravo, t.charle, t.delta)
    FROM #someTable t;
    
    PRINT DATEDIFF(ms, @st, getdate());
    GO
    
    PRINT char(10)+char(13)+'mtvf'+char(10)+char(13)+replicate('-',60);
    GO
    DECLARE @st datetime = getdate(), @z float;
    
    SELECT @z = f.newValue
    FROM #someTable t
    CROSS APPLY dbo.Compute_value_mtvf(t.alpha, t.bravo, t.charle, t.delta) f;
    
    PRINT DATEDIFF(ms, @st, getdate());
    GO
    
    PRINT char(10)+char(13)+'itvf'+char(10)+char(13)+replicate('-',60);
    GO
    DECLARE @st datetime = getdate(), @z float;
    
    SELECT @z = f.newValue
    FROM #someTable t
    CROSS APPLY dbo.Compute_value_itvf(t.alpha, t.bravo, t.charle, t.delta) f;
    
    PRINT DATEDIFF(ms, @st, getdate());
    GO
    

    結果:

    scalar
    ------------------------------------------------------------
    2786
    
    mTVF
    ------------------------------------------------------------
    41536
    
    iTVF
    ------------------------------------------------------------
    153
    

    スカラーudfは2.7秒間、mtvfでは41秒間、iTVFでは0.153秒間実行されました。推定実行計画を見てみましょう理由を理解するには:

    実際の実行プランを見るとこれはわかりませんが、スカラーudfとmtvfを使用すると、オプティマイザーは行ごとに実行が不十分なサブルーチンを呼び出します。 iTVFはそうではありません。 PaulWhiteの転職を引用 APPLYに関する記事 ポールはこう書いています:

    言い換えると、iTVFを使用すると、オプティマイザーを使用して、他のすべてのコードを実行する必要がある場合には不可能な方法でクエリを最適化できます。 iTVFが優れている理由の他の多くの例の1つは、並列処理を可能にする前述の3つの関数型のうちの1つだけであるということです。各関数をもう一度実行してみましょう。今回は、実際の実行プランをオンにして、traceflag 8649(並列実行プランを強制する)を使用します。

    -- don't need so many rows for this test
    TRUNCATE TABLE #sometable;
    INSERT #someTable 
    SELECT TOP (10)
      abs(checksum(newid())%10)+1, abs(checksum(newid())%10)+1, 
      abs(checksum(newid())%10)+1, abs(checksum(newid())%10)+1
    FROM sys.all_columns a;
    
    DECLARE @x float;
    
    SELECT TOP (10) @x = dbo.Compute_value_scalar(t.alpha, t.bravo, t.charle, t.delta)
    FROM #someTable t
    ORDER BY dbo.Compute_value_scalar(t.alpha, t.bravo, t.charle, t.delta)
    OPTION (QUERYTRACEON 8649);
    
    SELECT TOP (10)  @x = f.newValue
    FROM #someTable t
    CROSS APPLY dbo.Compute_value_mtvf(t.alpha, t.bravo, t.charle, t.delta) f
    ORDER BY f.newValue
    OPTION (QUERYTRACEON 8649);
    
    SELECT @x = f.newValue
    FROM #someTable t
    CROSS APPLY dbo.Compute_value_itvf(t.alpha, t.bravo, t.charle, t.delta) f
    ORDER BY f.newValue
    OPTION (QUERYTRACEON 8649);
    

    実行計画:

    iTVFの実行プランに表示される矢印は、並列処理です。つまり、すべてのCPU(またはSQLインスタンスのMAXDOPと同じ数)です。 設定により)一緒に作業できます。 T-SQLスカラーおよびmtvfUDFはそれを実行できません。マイクロソフトがインラインスカラーUDFを導入するとき、私はあなたがしていることのためにそれらを提案しますが、それまでは:パフォーマンスがあなたが探しているものであるなら、インラインが唯一の道であり、そのために、iTVFは唯一のゲームです市内。

    T-SQLを継続的に強調していることに注意してください 関数について話すとき...CLRスカラーとテーブル値の関数は問題ない場合がありますが、それは別のトピックです。




    1. MySQL-レコードの行番号を選択します

    2. SQLServerトリガーループ

    3. PostgresBDR9.4.1を使用したDjango1.8の移行

    4. なぜフレームワークを使用するのですか? ZendFrameworkを理解して使用する必要があると私に納得させてください