ストアドプロシージャに整数のリストを渡すために、 ユーザー定義のテーブルタイプ を使用しています。
次に、これらを使用して、ストアドプロシージャクエリの他のテーブルに結合します。
例えば:
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。整数以外のものも渡す必要がある場合があるので、コンマ区切りのリストなどを使用しないのはなぜですか。
このトピックは以前に説明されています。 JOINのパフォーマンスが低い主な理由は、テーブル値パラメーター(TVP)がテーブル変数であることです。テーブル変数は統計を保持せず、クエリオプティマイザーからは1行しかないように見えます。したがって、INSERT INTO Table (column_list) SELECT column_list FROM @TVP;
のように実行しても問題はありませんが、JOINではありません。
これを回避しようとするいくつかの事柄があります:
すべてをローカルの一時テーブルにダンプします(すでにこれを行っています)。ここでの技術的な欠点は、TVPに渡されたデータをtempdb
に複製していることです(TVPと一時テーブルの両方にデータが格納されています)。
クラスター化された主キーを持つようにユーザー定義のテーブルタイプを定義してみてください。これは_[Id]
_フィールドでインラインで実行できます。
_[ID] INT NOT NULL PRIMARY KEY
_
これがパフォーマンスにどの程度役立つかはわかりませんが、試してみる価値はあります。
クエリにOPTION (RECOMPILE)
を追加してみてください。これは、クエリオプティマイザーがテーブル変数の行数を確認して、適切な見積もりを取得できるようにする方法です。
_SELECT column_list
FROM SOMETABLE
INNER JOIN @Ids [OurTableType]
ON [OurTableType].Id = SOMETABLE.Id
OPTION (RECOMPILE);
_
ここでの欠点は、このプロシージャが呼び出されるたびに追加の時間がかかるRECOMPILE
があることです。しかし、それは全体的な純利益かもしれません。
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は例外です)。
SQL Server 2019では、「 テーブル変数の遅延コンパイル 」が導入されています。
テーブル変数の据え置きコンパイルでは、テーブル変数を参照するステートメントのコンパイルは、ステートメントが最初に実際に実行されるまで据え置かれます。この遅延コンパイルの動作は、一時テーブルの動作と同じです。この変更により、元の1行の推測ではなく、実際のカーディナリティが使用されます。
詳細については、リンクされたドキュメントを参照してください。
PS。 _SELECT *
_は行わないでください。常に列リストを指定します。 IF EXIST(SELECT * FROM)...
のようなことをしない限り。