SQL Server の一時テーブルとテーブル変数の違いは何ですか?
-
09-06-2019 - |
質問
SQL Server 2005 では、次の 2 つの方法のいずれかで一時テーブルを作成できます。
declare @tmp table (Col1 int, Col2 int);
または
create table #tmp (Col1 int, Col2 int);
これら 2 つの違いは何ですか?@tmp がまだ tempdb を使用しているのか、それともすべてがメモリ内で行われるのかについて、矛盾する意見を読んだことがあります。
どのシナリオで一方が他方よりも優れたパフォーマンスを発揮しますか?
解決
以下の MSDN リンクに詳しく説明されているように、一時テーブル (#tmp) とテーブル変数 (@tmp) の間にはいくつかの違いがありますが、tempdb の使用は違いの 1 つではありません。
経験則として、小規模から中規模のデータおよび単純な使用シナリオの場合は、テーブル変数を使用する必要があります。(これは非常に広範なガイドラインであり、もちろん多くの例外があります。以下および次の記事を参照してください。)
どちらかを選択する際に考慮すべき点は次のとおりです。
一時テーブルは実際のテーブルであるため、CREATE INDEX などの操作を行うことができます。大量のデータがあり、インデックスによるアクセスの方が高速な場合は、一時テーブルが適しています。
テーブル変数には、PRIMARY KEY または UNIQUE 制約を使用してインデックスを付けることができます。(一意でないインデックスが必要な場合は、一意制約の最後の列として主キー列を含めるだけです。一意の列がない場合は、ID 列を使用できます。) SQL 2014 には一意でないインデックスもあります.
テーブル変数はトランザクションに関与せず、
SELECT
は暗黙的にNOLOCK
. 。トランザクションの動作は非常に役立ちます。たとえば、プロシージャの途中で ROLLBACK したい場合、そのトランザクション中に設定されたテーブル変数は引き続き設定されます。一時テーブルにより、ストアド プロシージャが頻繁に再コンパイルされる可能性があります。テーブル変数はそうではありません。
SELECT INTO を使用して一時テーブルを作成できます。これにより、書き込みが速くなり (アドホック クエリに適しています)、一時テーブルの構造を事前に定義する必要がないため、時間の経過とともに変化するデータ型に対処できる可能性があります。
関数からテーブル変数を渡すことができるため、ロジックのカプセル化と再利用がはるかに簡単になります (たとえば、文字列を任意の区切り文字で値のテーブルに分割する関数を作成します)。
ユーザー定義関数内でテーブル変数を使用すると、それらの関数をより幅広く使用できるようになります (詳細については、CREATE FUNCTION のドキュメントを参照してください)。関数を作成している場合は、やむを得ない必要がない限り、一時テーブルではなくテーブル変数を使用する必要があります。
テーブル変数と一時テーブルは両方とも tempdb に保存されます。ただし、テーブル変数 (2005 年以降) はデフォルトで現在のデータベースの照合順序になりますが、一時テーブルでは tempdb のデフォルトの照合順序が使用されます (参照)。つまり、一時テーブルを使用していてデータベースの照合順序が tempdb の照合順序と異なる場合は、照合順序の問題に注意する必要があり、一時テーブルのデータをデータベースのデータと比較する場合に問題が発生することを意味します。
グローバル一時テーブル (##tmp) は、すべてのセッションとユーザーが使用できる別のタイプの一時テーブルです。
さらに読む:
マーティン・スミスの素晴らしい答え dba.stackexchange.com で
2 つの違いに関する MSDN FAQ: https://support.microsoft.com/en-gb/kb/305977
記事: http://searchsqlserver.techtarget.com/tip/0,289483,sid87_gci1267047,00.html#
一時テーブルと一時変数の予期しない動作とパフォーマンスへの影響: ポール・ホワイトが SQLblog.com で語る
他のヒント
受け入れられた回答の主張を見ると、テーブル変数はロギングに参加しません。
伐採の量に何らかの違いがあるというのは一般に嘘のようです(少なくとも insert
/update
/delete
テーブル自体に対する操作はありますが、 見つけて以来 追加のシステム テーブルの更新により、ストアド プロシージャ内のキャッシュされた一時オブジェクトについては、この点で若干の違いがあることに注意してください)。
両方に対するロギング動作を調べました。 @table_variable
そして #temp
以下の操作の表を参照してください。
- 挿入の成功
- 制約違反によりステートメントがロールバックされた複数行挿入。
- アップデート
- 消去
- 割り当て解除
トランザクション ログ レコードは、すべての操作でほぼ同一でした。
テーブル変数のバージョンには実際にはいくつかの機能があります。 余分な ログエントリにエントリが追加される(その後削除される)ため、 sys.syssingleobjrefs
ただし、純粋にテーブル変数の内部名が消費するバイト数がベース テーブルよりも 236 バイト少ないため、全体的にログに記録されるバイト数が数バイト少なくなります。 #temp
テーブル (118 個減少) nvarchar
文字)。
再現する完全なスクリプト (シングル ユーザー モードで起動し、次のコマンドを使用してインスタンスで実行するのが最適です) sqlcmd
モード)
:setvar tablename "@T"
:setvar tablescript "DECLARE @T TABLE"
/*
--Uncomment this section to test a #temp table
:setvar tablename "#T"
:setvar tablescript "CREATE TABLE #T"
*/
USE tempdb
GO
CHECKPOINT
DECLARE @LSN NVARCHAR(25)
SELECT @LSN = MAX([Current LSN])
FROM fn_dblog(null, null)
EXEC(N'BEGIN TRAN StartBatch
SAVE TRAN StartBatch
COMMIT
$(tablescript)
(
[4CA996AC-C7E1-48B5-B48A-E721E7A435F0] INT PRIMARY KEY DEFAULT 0,
InRowFiller char(7000) DEFAULT ''A'',
OffRowFiller varchar(8000) DEFAULT REPLICATE(''B'',8000),
LOBFiller varchar(max) DEFAULT REPLICATE(cast(''C'' as varchar(max)),10000)
)
BEGIN TRAN InsertFirstRow
SAVE TRAN InsertFirstRow
COMMIT
INSERT INTO $(tablename)
DEFAULT VALUES
BEGIN TRAN Insert9Rows
SAVE TRAN Insert9Rows
COMMIT
INSERT INTO $(tablename) ([4CA996AC-C7E1-48B5-B48A-E721E7A435F0])
SELECT TOP 9 ROW_NUMBER() OVER (ORDER BY (SELECT 0))
FROM sys.all_columns
BEGIN TRAN InsertFailure
SAVE TRAN InsertFailure
COMMIT
/*Try and Insert 10 rows, the 10th one will cause a constraint violation*/
BEGIN TRY
INSERT INTO $(tablename) ([4CA996AC-C7E1-48B5-B48A-E721E7A435F0])
SELECT TOP (10) (10 + ROW_NUMBER() OVER (ORDER BY (SELECT 0))) % 20
FROM sys.all_columns
END TRY
BEGIN CATCH
PRINT ERROR_MESSAGE()
END CATCH
BEGIN TRAN Update10Rows
SAVE TRAN Update10Rows
COMMIT
UPDATE $(tablename)
SET InRowFiller = LOWER(InRowFiller),
OffRowFiller =LOWER(OffRowFiller),
LOBFiller =LOWER(LOBFiller)
BEGIN TRAN Delete10Rows
SAVE TRAN Delete10Rows
COMMIT
DELETE FROM $(tablename)
BEGIN TRAN AfterDelete
SAVE TRAN AfterDelete
COMMIT
BEGIN TRAN EndBatch
SAVE TRAN EndBatch
COMMIT')
DECLARE @LSN_HEX NVARCHAR(25) =
CAST(CAST(CONVERT(varbinary,SUBSTRING(@LSN, 1, 8),2) AS INT) AS VARCHAR) + ':' +
CAST(CAST(CONVERT(varbinary,SUBSTRING(@LSN, 10, 8),2) AS INT) AS VARCHAR) + ':' +
CAST(CAST(CONVERT(varbinary,SUBSTRING(@LSN, 19, 4),2) AS INT) AS VARCHAR)
SELECT
[Operation],
[Context],
[AllocUnitName],
[Transaction Name],
[Description]
FROM fn_dblog(@LSN_HEX, null) AS D
WHERE [Current LSN] > @LSN
SELECT CASE
WHEN GROUPING(Operation) = 1 THEN 'Total'
ELSE Operation
END AS Operation,
Context,
AllocUnitName,
COALESCE(SUM([Log Record Length]), 0) AS [Size in Bytes],
COUNT(*) AS Cnt
FROM fn_dblog(@LSN_HEX, null) AS D
WHERE [Current LSN] > @LSN
GROUP BY GROUPING SETS((Operation, Context, AllocUnitName),())
結果
+-----------------------+--------------------+---------------------------+---------------+------+---------------+------+------------------+
| | | | @TV | #TV | |
+-----------------------+--------------------+---------------------------+---------------+------+---------------+------+------------------+
| Operation | Context | AllocUnitName | Size in Bytes | Cnt | Size in Bytes | Cnt | Difference Bytes |
+-----------------------+--------------------+---------------------------+---------------+------+---------------+------+------------------+
| LOP_ABORT_XACT | LCX_NULL | | 52 | 1 | 52 | 1 | |
| LOP_BEGIN_XACT | LCX_NULL | | 6056 | 50 | 6056 | 50 | |
| LOP_COMMIT_XACT | LCX_NULL | | 2548 | 49 | 2548 | 49 | |
| LOP_COUNT_DELTA | LCX_CLUSTERED | sys.sysallocunits.clust | 624 | 3 | 624 | 3 | |
| LOP_COUNT_DELTA | LCX_CLUSTERED | sys.sysrowsets.clust | 208 | 1 | 208 | 1 | |
| LOP_COUNT_DELTA | LCX_CLUSTERED | sys.sysrscols.clst | 832 | 4 | 832 | 4 | |
| LOP_CREATE_ALLOCCHAIN | LCX_NULL | | 120 | 3 | 120 | 3 | |
| LOP_DELETE_ROWS | LCX_INDEX_INTERIOR | Unknown Alloc Unit | 720 | 9 | 720 | 9 | |
| LOP_DELETE_ROWS | LCX_MARK_AS_GHOST | sys.sysallocunits.clust | 444 | 3 | 444 | 3 | |
| LOP_DELETE_ROWS | LCX_MARK_AS_GHOST | sys.sysallocunits.nc | 276 | 3 | 276 | 3 | |
| LOP_DELETE_ROWS | LCX_MARK_AS_GHOST | sys.syscolpars.clst | 628 | 4 | 628 | 4 | |
| LOP_DELETE_ROWS | LCX_MARK_AS_GHOST | sys.syscolpars.nc | 484 | 4 | 484 | 4 | |
| LOP_DELETE_ROWS | LCX_MARK_AS_GHOST | sys.sysidxstats.clst | 176 | 1 | 176 | 1 | |
| LOP_DELETE_ROWS | LCX_MARK_AS_GHOST | sys.sysidxstats.nc | 144 | 1 | 144 | 1 | |
| LOP_DELETE_ROWS | LCX_MARK_AS_GHOST | sys.sysiscols.clst | 100 | 1 | 100 | 1 | |
| LOP_DELETE_ROWS | LCX_MARK_AS_GHOST | sys.sysiscols.nc1 | 88 | 1 | 88 | 1 | |
| LOP_DELETE_ROWS | LCX_MARK_AS_GHOST | sys.sysobjvalues.clst | 596 | 5 | 596 | 5 | |
| LOP_DELETE_ROWS | LCX_MARK_AS_GHOST | sys.sysrowsets.clust | 132 | 1 | 132 | 1 | |
| LOP_DELETE_ROWS | LCX_MARK_AS_GHOST | sys.sysrscols.clst | 528 | 4 | 528 | 4 | |
| LOP_DELETE_ROWS | LCX_MARK_AS_GHOST | sys.sysschobjs.clst | 1040 | 6 | 1276 | 6 | 236 |
| LOP_DELETE_ROWS | LCX_MARK_AS_GHOST | sys.sysschobjs.nc1 | 820 | 6 | 1060 | 6 | 240 |
| LOP_DELETE_ROWS | LCX_MARK_AS_GHOST | sys.sysschobjs.nc2 | 820 | 6 | 1060 | 6 | 240 |
| LOP_DELETE_ROWS | LCX_MARK_AS_GHOST | sys.sysschobjs.nc3 | 480 | 6 | 480 | 6 | |
| LOP_DELETE_ROWS | LCX_MARK_AS_GHOST | sys.syssingleobjrefs.clst | 96 | 1 | | | -96 |
| LOP_DELETE_ROWS | LCX_MARK_AS_GHOST | sys.syssingleobjrefs.nc1 | 88 | 1 | | | -88 |
| LOP_DELETE_ROWS | LCX_MARK_AS_GHOST | Unknown Alloc Unit | 72092 | 19 | 72092 | 19 | |
| LOP_DELETE_ROWS | LCX_TEXT_MIX | Unknown Alloc Unit | 16348 | 37 | 16348 | 37 | |
| LOP_FORMAT_PAGE | LCX_HEAP | Unknown Alloc Unit | 1596 | 19 | 1596 | 19 | |
| LOP_FORMAT_PAGE | LCX_IAM | Unknown Alloc Unit | 252 | 3 | 252 | 3 | |
| LOP_FORMAT_PAGE | LCX_INDEX_INTERIOR | Unknown Alloc Unit | 84 | 1 | 84 | 1 | |
| LOP_FORMAT_PAGE | LCX_TEXT_MIX | Unknown Alloc Unit | 4788 | 57 | 4788 | 57 | |
| LOP_HOBT_DDL | LCX_NULL | | 108 | 3 | 108 | 3 | |
| LOP_HOBT_DELTA | LCX_NULL | | 9600 | 150 | 9600 | 150 | |
| LOP_INSERT_ROWS | LCX_CLUSTERED | sys.sysallocunits.clust | 456 | 3 | 456 | 3 | |
| LOP_INSERT_ROWS | LCX_CLUSTERED | sys.syscolpars.clst | 644 | 4 | 644 | 4 | |
| LOP_INSERT_ROWS | LCX_CLUSTERED | sys.sysidxstats.clst | 180 | 1 | 180 | 1 | |
| LOP_INSERT_ROWS | LCX_CLUSTERED | sys.sysiscols.clst | 104 | 1 | 104 | 1 | |
| LOP_INSERT_ROWS | LCX_CLUSTERED | sys.sysobjvalues.clst | 616 | 5 | 616 | 5 | |
| LOP_INSERT_ROWS | LCX_CLUSTERED | sys.sysrowsets.clust | 136 | 1 | 136 | 1 | |
| LOP_INSERT_ROWS | LCX_CLUSTERED | sys.sysrscols.clst | 544 | 4 | 544 | 4 | |
| LOP_INSERT_ROWS | LCX_CLUSTERED | sys.sysschobjs.clst | 1064 | 6 | 1300 | 6 | 236 |
| LOP_INSERT_ROWS | LCX_CLUSTERED | sys.syssingleobjrefs.clst | 100 | 1 | | | -100 |
| LOP_INSERT_ROWS | LCX_CLUSTERED | Unknown Alloc Unit | 135888 | 19 | 135888 | 19 | |
| LOP_INSERT_ROWS | LCX_INDEX_INTERIOR | Unknown Alloc Unit | 1596 | 19 | 1596 | 19 | |
| LOP_INSERT_ROWS | LCX_INDEX_LEAF | sys.sysallocunits.nc | 288 | 3 | 288 | 3 | |
| LOP_INSERT_ROWS | LCX_INDEX_LEAF | sys.syscolpars.nc | 500 | 4 | 500 | 4 | |
| LOP_INSERT_ROWS | LCX_INDEX_LEAF | sys.sysidxstats.nc | 148 | 1 | 148 | 1 | |
| LOP_INSERT_ROWS | LCX_INDEX_LEAF | sys.sysiscols.nc1 | 92 | 1 | 92 | 1 | |
| LOP_INSERT_ROWS | LCX_INDEX_LEAF | sys.sysschobjs.nc1 | 844 | 6 | 1084 | 6 | 240 |
| LOP_INSERT_ROWS | LCX_INDEX_LEAF | sys.sysschobjs.nc2 | 844 | 6 | 1084 | 6 | 240 |
| LOP_INSERT_ROWS | LCX_INDEX_LEAF | sys.sysschobjs.nc3 | 504 | 6 | 504 | 6 | |
| LOP_INSERT_ROWS | LCX_INDEX_LEAF | sys.syssingleobjrefs.nc1 | 92 | 1 | | | -92 |
| LOP_INSERT_ROWS | LCX_TEXT_MIX | Unknown Alloc Unit | 5112 | 71 | 5112 | 71 | |
| LOP_MARK_SAVEPOINT | LCX_NULL | | 508 | 8 | 508 | 8 | |
| LOP_MODIFY_COLUMNS | LCX_CLUSTERED | Unknown Alloc Unit | 1560 | 10 | 1560 | 10 | |
| LOP_MODIFY_HEADER | LCX_HEAP | Unknown Alloc Unit | 3780 | 45 | 3780 | 45 | |
| LOP_MODIFY_ROW | LCX_CLUSTERED | sys.syscolpars.clst | 384 | 4 | 384 | 4 | |
| LOP_MODIFY_ROW | LCX_CLUSTERED | sys.sysidxstats.clst | 100 | 1 | 100 | 1 | |
| LOP_MODIFY_ROW | LCX_CLUSTERED | sys.sysrowsets.clust | 92 | 1 | 92 | 1 | |
| LOP_MODIFY_ROW | LCX_CLUSTERED | sys.sysschobjs.clst | 1144 | 13 | 1144 | 13 | |
| LOP_MODIFY_ROW | LCX_IAM | Unknown Alloc Unit | 4224 | 48 | 4224 | 48 | |
| LOP_MODIFY_ROW | LCX_PFS | Unknown Alloc Unit | 13632 | 169 | 13632 | 169 | |
| LOP_MODIFY_ROW | LCX_TEXT_MIX | Unknown Alloc Unit | 108640 | 120 | 108640 | 120 | |
| LOP_ROOT_CHANGE | LCX_CLUSTERED | sys.sysallocunits.clust | 960 | 10 | 960 | 10 | |
| LOP_SET_BITS | LCX_GAM | Unknown Alloc Unit | 1200 | 20 | 1200 | 20 | |
| LOP_SET_BITS | LCX_IAM | Unknown Alloc Unit | 1080 | 18 | 1080 | 18 | |
| LOP_SET_BITS | LCX_SGAM | Unknown Alloc Unit | 120 | 2 | 120 | 2 | |
| LOP_SHRINK_NOOP | LCX_NULL | | | | 32 | 1 | 32 |
+-----------------------+--------------------+---------------------------+---------------+------+---------------+------+------------------+
| Total | | | 410144 | 1095 | 411232 | 1092 | 1088 |
+-----------------------+--------------------+---------------------------+---------------+------+---------------+------+------------------+
どのシナリオで一方が他方よりも優れたパフォーマンスを発揮しますか?
小さいテーブル (1000 行未満) の場合は一時変数を使用し、それ以外の場合は一時テーブルを使用します。
@wcm - 実際にテーブル変数を選択するのはRamだけではありません - 部分的にディスクに保存できます。
一時テーブルにはインデックスを含めることができますが、テーブル変数にはプライマリ インデックスのみを含めることができます。速度が問題になる場合は、テーブル変数の方が高速である可能性がありますが、大量のレコードがある場合、またはクラスター化インデックスの一時テーブルを検索する必要がある場合は、明らかに一時テーブルの方が優れています。
一時テーブル:一時テーブルは、データの作成とバックアップが簡単です。
テーブル変数:しかし、テーブル変数は通常のテーブルを作成するときに手間がかかります。
一時テーブル:一時テーブルの結果は複数のユーザーが使用できます。
テーブル変数:ただし、テーブル変数を使用できるのは現在のユーザーのみです。
一時テーブル:一時テーブルは tempdb に保存されます。ネットワークトラフィックが発生します。一時テーブルに大きなデータがある場合は、データベース全体で機能する必要があります。パフォーマンスの問題が発生します。
テーブル変数:ただし、テーブル変数は一部のデータを物理メモリに保存し、後でサイズが増加すると tempdb に移動します。
一時テーブル:一時テーブルはすべての DDL 操作を実行できます。インデックスの作成、削除、変更などが可能になります。
テーブル変数:一方、テーブル変数では DDL 操作を実行できません。ただし、テーブル変数を使用して作成できるのはクラスター化インデックスのみです。
一時テーブル:一時テーブルは、現在のセッションまたはグローバルに使用できます。これにより、複数のユーザー セッションでテーブル内の結果を利用できるようになります。
テーブル変数:ただし、テーブル変数はそのプログラムまで使用できます。(ストアドプロシージャ)
一時テーブル:一時変数はトランザクションを使用できません。一時テーブルで DML 操作を実行すると、トランザクションをロールバックまたはコミットできます。
テーブル変数:ただし、テーブル変数に対してはそれができません。
一時テーブル:関数では temp 変数を使用できません。さらに、関数内で DML 操作を行うことはできません。
テーブル変数:ただし、この関数を使用するとテーブル変数を使用できます。しかし、テーブル変数を使用するとそれが可能になります。
一時テーブル:後続の呼び出しごとに temp 変数を使用する場合、ストアド プロシージャは再コンパイルを実行します (同じ実行プランは使用できません)。
テーブル変数:一方、テーブル変数はそのようにはなりません。
一時変数はメモリ内にのみあるという通説を信じている皆さんへ
まず、テーブル変数は必ずしもメモリに常駐するとは限りません。メモリ不足が発生すると、テーブル変数に属するページが tempdb にプッシュされる可能性があります。
ここで記事を読んでください: 一時DB::テーブル変数とローカル一時テーブル
もう 1 つの主な違いは、一時テーブルとは異なり、テーブル変数には列の統計情報がないことです。これは、クエリ オプティマイザーがテーブル変数に行数がいくつあるかを認識していない (1 と推測する) ことを意味します。そのため、テーブル変数に実際に多数の行がある場合、非常に非最適なプランが生成される可能性があります。
引用元; プロフェッショナル SQL Server 2012 の内部構造とトラブルシューティング
統計温度テーブルとテーブル変数の主な違いは、統計がテーブル変数に作成されないことです。これには2つの主要な結果がありますが、その最初の結果は、クエリオプティマイザーが、含まれるデータに関係なく、テーブル変数の行数に対してfi xed推定を使用することです。さらに、データを追加または削除しても、推定値は変更されません。
インデックス 制約を作成することはできますが、テーブル変数にインデックスを作成することはできません。これは、主要なキーまたは一意の制約を作成することにより、テーブル変数にインデックス(これらが制約をサポートするために作成されているため)を持つことができることを意味します。制約があり、したがって統計があるインデックスがある場合でも、コンパイル時に存在しないため、クエリがコンパイルされたときにインデックスは使用されず、再コンパイルを引き起こすこともありません。
スキーマの変更 一時的なテーブルではスキーマの変更が可能ですが、テーブル変数では変更できません。一時的なテーブルではスキーマ修飾が可能ですが、テーブルを使用するステートメントの再コンパイルを引き起こすため、それらを使用しないでください。
テーブル変数はメモリ内に作成されません
テーブル変数はメモリ内構造であり、一時的なテーブルよりも速く実行されるという一般的な誤解があります. 。SYSというDMVに感謝します。dm _ db _セッション_スペース_使用法では、セッションごとにtempdbの使用が示されています。 そうではないことを証明できます. 。SQL Serverを再起動してDMVをクリアした後、次のスクリプトを実行して、セッション_ IDがユーザー_オブジェクト_ Alloc _ Page _ Countの0を返すことを確認してください。
SELECT session_id,
database_id,
user_objects_alloc_page_count
FROM sys.dm_db_session_space_usage
WHERE session_id > 50 ;
次のスクリプトを実行して1つの列を使用して一時テーブルを作成し、1つの行を入力することにより、一時テーブルが使用するスペースを確認できます。
CREATE TABLE #TempTable ( ID INT ) ;
INSERT INTO #TempTable ( ID )
VALUES ( 1 ) ;
GO
SELECT session_id,
database_id,
user_objects_alloc_page_count
FROM sys.dm_db_session_space_usage
WHERE session_id > 50 ;
私のサーバー上の結果は、テーブルが tempdb 内の 1 ページに割り当てられたことを示しています。ここで同じスクリプトを実行しますが、今回はテーブル変数を使用します。
DECLARE @TempTable TABLE ( ID INT ) ;
INSERT INTO @TempTable ( ID )
VALUES ( 1 ) ;
GO
SELECT session_id,
database_id,
user_objects_alloc_page_count
FROM sys.dm_db_session_space_usage
WHERE session_id > 50 ;
どちらを使用しますか?
一時テーブルまたはテーブル変数を使用するかどうかは、徹底的なテストによって決定されるべきですが、 一時的なものに頼るのが最善です テーブルをデフォルトとして使用するのは、使用できるものがはるかに少ないためです。 間違っている.
テーブル変数を使用してコードを開発しているのを見てきました。少量の行を扱っていて、一時的なテーブルよりも速かったですが、数年後にはテーブル変数に数十万の行があり、パフォーマンスはひどいものでした。 、だから、あなたがあなたの決定を下すときに何らかの能力計画を試してみてください!
もう一つの違い:
テーブル変数には、それを作成するプロシージャ内のステートメントからのみアクセスできます。そのプロシージャによって呼び出される他のプロシージャや、ネストされた動的 SQL (exec または sp_executesql 経由) からはアクセスできません。
一方、一時テーブルのスコープには、呼び出されるプロシージャとネストされた動的 SQL 内のコードが含まれます。
プロシージャによって作成されたテーブルに、呼び出される他のプロシージャまたは動的 SQL からアクセスできる必要がある場合は、一時テーブルを使用する必要があります。これは複雑な状況で非常に便利です。
また、多くの場合、両方を派生テーブルに置き換えることで、より高速になる可能性があることも考慮してください。ただし、すべてのパフォーマンス チューニングと同様に、特定のクエリに対する最適なアプローチを知ることができるのは、実際のデータに対する実際のテストだけです。