web-dev-qa-db-ja.com

SQL Serverでのユーザー定義テーブルタイプのパフォーマンス

ストアドプロシージャに整数のリストを渡すために、 ユーザー定義のテーブルタイプ を使用しています。

次に、これらを使用して、ストアドプロシージャクエリの他のテーブルに結合します。

例えば:

CREATE PROCEDURE [dbo].[sp_Name]
(
    @Ids [dbo].[OurTableType] READONLY  
)
AS
    SET Nocount ON

    SELECT
        *
    FROM
        SOMETABLE
        INNER JOIN @Ids [OurTableType] ON [OurTableType].Id = SOMETABLE.Id

これより大きなデータセットを使用すると、パフォーマンスが非常に低下します。

速度を上げるために使用したアプローチの1つは、コンテンツを一時テーブルにダンプし、代わりに結合することです。

例えば:

CREATE PROCEDURE [dbo].[sp_Name]
(
    @Ids [dbo].[OurTableType] READONLY  
)
AS
    SET Nocount ON
    CREATE TABLE #TempTable(Id INT)
    INSERT INTO #TempTable
    SELECT Id from @Ids

    SELECT
        *
    FROM
        SOMETABLE
        INNER JOIN #TempTable ON #TempTable.Id = SOMETABLE.Id

    DROP TABLE #TempTable

これによりパフォーマンスは大幅に向上しますが、このアプローチと、私たちが考慮していないその他の結果について、いくつかの意見を得たかったのです。また、これによりパフォーマンスが向上する理由についての説明も役立ちます。

N.B。整数以外のものも渡す必要がある場合があるので、コンマ区切りのリストなどを使用しないのはなぜですか。

14
KMB

このトピックは以前に説明されています。 JOINのパフォーマンスが低い主な理由は、テーブル値パラメーター(TVP)がテーブル変数であることです。テーブル変数は統計を保持せず、クエリオプティマイザーからは1行しかないように見えます。したがって、INSERT INTO Table (column_list) SELECT column_list FROM @TVP;のように実行しても問題はありませんが、JOINではありません。

これを回避しようとするいくつかの事柄があります:

  1. すべてをローカルの一時テーブルにダンプします(すでにこれを行っています)。ここでの技術的な欠点は、TVPに渡されたデータをtempdbに複製していることです(TVPと一時テーブルの両方にデータが格納されています)。

  2. クラスター化された主キーを持つようにユーザー定義のテーブルタイプを定義してみてください。これは_[Id]_フィールドでインラインで実行できます。

    _[ID] INT NOT NULL PRIMARY KEY
    _

    これがパフォーマンスにどの程度役立つかはわかりませんが、試してみる価値はあります。

  3. クエリにOPTION (RECOMPILE)を追加してみてください。これは、クエリオプティマイザーがテーブル変数の行数を確認して、適切な見積もりを取得できるようにする方法です。

    _SELECT column_list
    FROM   SOMETABLE
    INNER JOIN @Ids [OurTableType]
            ON [OurTableType].Id = SOMETABLE.Id
    OPTION (RECOMPILE);
    _

    ここでの欠点は、このプロシージャが呼び出されるたびに追加の時間がかかるRECOMPILEがあることです。しかし、それは全体的な純利益かもしれません。

  4. SQL Server 2014以降では、インメモリOLTPを利用して、ユーザー定義のテーブルタイプにWITH (MEMORY_OPTIMIZED = ON)を指定できます。次を参照してください シナリオ:テーブル詳細については、変数をMEMORY_OPTIMIZED = ON にすることができます。これは間違いなく役立つと聞きました。残念ながら、SQL Server 2014およびSQL Server 2016ではRTMこの機能は64ビットエンタープライズでのみ使用できますエディション。ただし、SQL Server 2016 SP1以降、この機能はすべてのエディションで利用可能になりました(SQL Server Express LocalDBは例外です)。

  5. SQL Server 2019では、「 テーブル変数の遅延コンパイル 」が導入されています。

    テーブル変数の据え置きコンパイルでは、テーブル変数を参照するステートメントのコンパイルは、ステートメントが最初に実際に実行されるまで据え置かれます。この遅延コンパイルの動作は、一時テーブルの動作と同じです。この変更により、元の1行の推測ではなく、実際のカーディナリティが使用されます。

    詳細については、リンクされたドキュメントを参照してください。

PS。 _SELECT *_は行わないでください。常に列リストを指定します。 IF EXIST(SELECT * FROM)...のようなことをしない限り。

18
Solomon Rutzky