SQL Serverの一時テーブルとテーブル変数の違いは何ですか?


390

SQL Server 2005では、次の2つの方法のいずれかで一時テーブルを作成できます。

declare @tmp table (Col1 int, Col2 int);

または

create table #tmp (Col1 int, Col2 int);

これら2つの違いは何ですか?@tmpがtempdbをまだ使用しているかどうか、またはすべてがメモリ内で発生するかどうかについて、私は矛盾する意見を読みました。

どちらが優れているのか?



2
Pinalデイブによって本当に良い書き込みアップはここにあります... blog.sqlauthority.com/2009/12/15/...は
SAM YI

回答:


392

tempdbの使用はそれらの1つではありませんが、一時テーブル(#tmp)とテーブル変数(@tmp)の間にはいくつかの違いがあります。

経験則として、少量から中規模のデータおよび単純な使用シナリオでは、テーブル変数を使用する必要があります。(これは非常に広範なガイドラインですが、もちろん多くの例外があります。以下および以下の記事を参照してください。)

それらから選択する際に考慮すべきいくつかのポイント:

  • 一時テーブルは実際のテーブルなので、CREATE INDEXなどを実行できます。大量のデータがあり、インデックスによるアクセスが高速な場合は、一時テーブルが適切なオプションです。

  • テーブル変数は、PRIMARY KEYまたはUNIQUE制約を使用してインデックスを持つことができます。(一意でないインデックスが必要な場合は、主キー列をUNIQUE制約の最後の列として含めるだけです。一意の列がない場合は、ID列を使用できます。)SQL 2014にも一意でないインデックスがあります。

  • テーブル変数はトランザクションに参加せず、SELECTは暗黙的にで使用されNOLOCKます。トランザクションの動作は非常に役立ちます。たとえば、プロシージャの途中でROLLBACKしたい場合、そのトランザクション中に入力されたテーブル変数は引き続き入力されます。

  • 一時テーブルにより、ストアドプロシージャが再コンパイルされる可能性があります。テーブル変数はしません。

  • SELECT INTOを使用して一時テーブルを作成できます。これは書き込みが速く(アドホッククエリに適しています)、一時テーブルの構造を事前に定義する必要がないため、時間の経過に伴うデータ型の変化に対応できる場合があります。

  • 関数からテーブル変数を返すことができるため、ロジックをより簡単にカプセル化して再利用できます(たとえば、文字列を任意の区切り文字で値のテーブルに分割する関数を作成できます)。

  • ユーザー定義関数内でテーブル変数を使用すると、それらの関数をより広く使用できます(詳細については、CREATE FUNCTIONのドキュメントを参照してください)。関数を作成している場合は、特に必要がない限り、一時テーブルではなくテーブル変数を使用する必要があります。

  • テーブル変数と一時テーブルの両方がtempdbに格納されます。ただし、テーブル変数(2005以降)は、現在のデータベースの照合に対してデフォルトで設定されます。tempdbのデフォルトの照合を使用する一時テーブル(ref)に対して照合が行われます。つまり、一時テーブルを使用していて、db照合がtempdbと異なる場合は、照合の問題に注意する必要があります。これにより、一時テーブルのデータをデータベースのデータと比較する場合に問題が発生します。

  • グローバル一時テーブル(## tmp)は、すべてのセッションとユーザーが使用できる別のタイプの一時テーブルです。

さらに読む:


26
テーブル変数はインデックスを持つことができます。一意の制約を作成するだけで、自動的にインデックスが取得されます。パフォーマンスに大きな違いをもたらします。(一意のインデックスが必要ない場合は、必要なフィールドの最後に実際の主キーを追加します。まだない場合は、ID列を作成します)。
ベン・

7
@Ben And SQL Server 2014では、テーブル変数に
Martin Smith

4
トランザクションの影響を受けていないテーブル変数が便利な場合があります。ロールバック後に保持したいものがあれば、それをテーブル変数に入れることができます。
キルブレーカー、2013年

3
統計は一時テーブル用に作成されます。これによりクエリプランが改善されますが、テーブル変数用ではありません。これらの統計は、一時テーブルが削除された後、一時テーブルのページとともにしばらくの間キャッシュされ、キャッシュされたテーブルを再度アクティブ化すると不正確になる可能性があります。
Michael Green 14

テーブル変数は、ユーザー定義のデータ型の照合順序(列がユーザー定義のデータ型の場合)またはtempdbの既定の照合順序ではなく、現在のデータベースの照合順序にデフォルト設定されます。一時テーブルはtempdbのデフォルトの照合を使用します。参照:technet.microsoft.com/en-us/library/ms188927.aspx
PseudoToad

25

受け入れられた回答で、テーブル変数はロギングに参加しないという主張を見るだけです。

ログの量に違いがあることは、一般的には正しくないようです(少なくとも、テーブル自体へのinsert/ update/ delete操作の場合)。ただし、システムテーブルの追加により、ストアドプロシージャのキャッシュされた一時オブジェクトについて、この点で多少の違いがあることがわかりました。アップデート)。

次の操作について、@table_variable#tempテーブルの両方に対するロギング動作を確認しました。

  1. 成功した挿入
  2. 制約違反のためにステートメントがロールバックされた複数行挿入のwhere。
  3. 更新
  4. 削除する
  5. 割り当て解除

トランザクションログレコードは、すべての操作でほぼ同じでした。

テーブル変数バージョンは、ベーステーブルに追加された(後で削除された)エントリを取得するため、実際にはいくつかの余分なログエントリがあり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             |
+-----------------------+--------------------+---------------------------+---------------+------+---------------+------+------------------+

1
+1好奇心から外れた(そして少し知識を深める)。質問はかなり古い(2008年8月)ので、SQL 2005に関するものでした。現在、2011年(終わり)であり、最新のSQLは2008 R2とデナリベータ版です。どのバージョンを使用しましたか?
xanatos

2
@xanatos-2008。2005 INSERT ... SELECTではSELECT INTO ... 、テーブル変数は最小限のログに記録されず、テーブル変数を使用できないため、実際には不利になります。
マーティン・スミス

1
@MartinSmithに感謝します。ロギングに関する主張を削除するために私の回答を更新しました。
Rory

18

どちらが優れているのか?

小さいテーブル(1000行未満)の場合は一時変数を使用し、それ以外の場合は一時テーブルを使用します。


17
サポートデータはありますか?これは、それ自体のアサーションとしてはあまり役に立ちません。
マイケル・マイヤーズ

8
マイクロソフトは100行の制限をお勧めします:msdn.microsoft.com/en-us/library/ms175010.aspx(「ベストプラクティス」セクションを参照)。
Artemix 2013年

説明については、以下の私の回答を参照してください。
Weihui Guo

17

@wcm-実際にテーブル変数を選択するのはRamだけではありません-ディスクに部分的に保存できます。

一時テーブルはインデックスを持つことができますが、テーブル変数はプライマリインデックスしか持つことができません。速度が問題になる場合は、テーブル変数の方が高速ですが、レコードが多い場合や、クラスター化インデックスの一時テーブルを検索する必要がある場合は、一時テーブルの方が適しています。

良い背景記事


2
良い背景記事+1。私はそれがあまりを残していないだろうと非常に多くの良い答えが既に存在している修正するので、私の答えを削除することがあります
WCM

12
  1. 一時テーブル:一時テーブルはデータの作成とバックアップが簡単です。

    テーブル変数:ただし、テーブル変数には、通常のテーブルを通常作成するときの労力が含まれます。

  2. 一時テーブル:一時テーブルの結果は複数のユーザーが使用できます。

    テーブル変数:ただし、テーブル変数は現在のユーザーのみが使用できます。 

  3. 一時テーブル:一時テーブルはtempdbに格納されます。ネットワークトラフィックが発生します。一時テーブルに大きなデータがある場合、データベース全体で機能する必要があります。パフォーマンスの問題が存在します。

    テーブル変数:ただし、テーブル変数は一部のデータの物理メモリに格納され、後でサイズが増加するとtempdbに移動されます。

  4. 一時テーブル:一時テーブルはすべてのDDL操作を実行できます。インデックスの作成、削除、変更などが可能です。

    テーブル変数:テーブル変数ではDDL操作を実行できません。ただし、テーブル変数を使用すると、クラスター化インデックスのみを作成できます。

  5. 一時テーブル:一時テーブルは、現在のセッションまたはグローバルで使用できます。そのため、複数のユーザーセッションでテーブルの結果を利用できます。

    テーブル変数:ただし、テーブル変数はそのプログラムまで使用できます。(ストアドプロシージャ)

  6. 一時テーブル:一時変数はトランザクションを使用できません。一時テーブルを使用してDML操作を実行すると、トランザクションをロールバックまたはコミットできます。

    テーブル変数:ただし、テーブル変数に対しては実行できません。

  7. 一時テーブル:関数は一時変数を使用できません。さらに、関数でDML操作を行うことはできません。

    テーブル変数:ただし、この関数ではテーブル変数を使用できます。しかし、テーブル変数を使用すると、それを行うことができます。

  8. 一時テーブル:後続のすべての呼び出しで一時変数を使用すると、ストアドプロシージャは再コンパイルを行います(同じ実行プランは使用できません)。

    テーブル変数:テーブル変数はそのように動作しませんが。


8

一時変数はメモリにのみ存在するという神話を信じているすべての人にとって

まず、テーブル変数は必ずしもメモリに常駐しているわけではありません。メモリ不足の場合、テーブル変数に属するページをtempdbにプッシュできます。

ここの記事を読んでください:TempDB ::テーブル変数vsローカル一時テーブル


3
回答を編集して、2つのポイントに対応する単一の回答にできますか?
ジョシュアドレイク

7

その他の主な違いは、一時テーブルとは異なり、テーブル変数に列統計がないことです。これは、クエリオプティマイザーがテーブル変数の行数(1を推測)を認識していないことを意味します。これにより、テーブル変数に実際に多数の行がある場合、最適ではないプランが生成される可能性があります。


2
rowssys.partitionsはテーブル変数用に維持されるため、実際にはテーブル内の行数がわかります。これはを使用して確認できますOPTION (RECOMPILE)。ただし、列の統計がないため、特定の列の述部を推定できません。
マーティン・スミス

7

引用元; Professional SQL Server 2012の内部とトラブルシューティング

統計 一時テーブルとテーブル変数の主な違いは、統計がテーブル変数で作成されないことです。これには2つの大きな影響があります。1つ目は、クエリオプティマイザーが、含まれているデータに関係なく、テーブル変数の行数に固定推定値を使用することです。さらに、データを追加または削除しても、推定値は変わりません。

インデックス制約を作成することはできますが、テーブル変数にインデックスを作成することはできません。つまり、主キーまたは一意の制約を作成することで、テーブル変数にインデックスを作成できます(これらは制約をサポートするために作成されるため)。制約があり、したがって統計を持つインデックスがある場合でも、インデックスはコンパイル時に存在しないため、クエリのコンパイル時に使用されず、再コンパイルも発生しません。

スキーマの変更スキーマの変更は一時テーブルでは可能ですが、テーブル変数ではできません。スキーマの変更は一時テーブルで可能ですが、テーブルを使用するステートメントの再コンパイルが発生するため、使用しないでください。

一時テーブルとテーブル変数

テーブル変数はメモリに作成されません

テーブル変数はメモリ内構造であるため、一時テーブルよりも高速に実行されるという一般的な誤解があります。sysというDMVに感謝します。dm _ db _セッション_スペース_使用状況。これは、セッションごとのtempdbの使用状況を示しますが、そうでないことを証明できます。SQL Serverを再起動してDMVをクリアした後、次のスクリプトを実行して、セッション_ idがユーザー_オブジェクト_ alloc _ページ_ 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 ;

どちらを使用しますか?

一時テーブルまたはテーブル変数を使用するかどうかは、十分なテストによって決定する必要がありますが、問題が発生する可能性がはるかに少ないため、デフォルトとして一時 テーブルを使用することをお勧めし ます

少量の行を処理していたため、テーブル変数を使用してコードを開発している顧客を見てきました。一時テーブルよりも高速でしたが、数年後、テーブル変数に数十万行があり、パフォーマンスはひどいものでした。 、それであなたの決定をするときにいくつかの容量計画を試してみてください!


実際には統計がテーブル変数に作成され、参照stackoverflow.com/questions/42824366/...
ユーフェンシェン

4

別の違い:

テーブルvarには、それを作成するプロシージャ内のステートメントからのみアクセスできます。そのプロシージャから呼び出された他のプロシージャやネストされた動的SQL(execまたはsp_executesqlを介して)からはアクセスできません。

一方、一時テーブルのスコープには、呼び出されたプロシージャとネストされた動的SQLのコードが含まれます。

プロシージャによって作成されたテーブルに他の呼び出されたプロシージャまたは動的SQLからアクセスできる必要がある場合は、一時テーブルを使用する必要があります。これは複雑な状況で非常に便利です。


2

違いTemporary Tables (##temp/#temp)とはTable Variables (@table)のとおりです。

  1. Table variable (@table)で作成されますmemory。一方、はでTemporary table (##temp/#temp)作成されますtempdb database。ただし、メモリが不足している場合、テーブル変数に属するページがtempdbにプッシュされることがあります。

  2. Table variablesに関与することはできませんtransactions, logging or locking。これはになり@table faster then #tempます。したがって、テーブル変数は一時テーブルよりも高速です。

  3. Temporary tableとは異なり、スキーマの変更を許可しますTable variables

  4. Temporary tables作成されたルーチンと子ルーチンにも表示されます。一方、テーブル変数は、作成されたルーチンでのみ表示されます。

  5. Temporary tablesは許可されCREATE INDEXesますが、代わりにを使用してインデックスを持つことはできTable variablesません。CREATE INDEXPrimary Key or Unique Constraint


1

多くの場合、両方をより高速な派生テーブルに置き換えることができることも考慮してください。ただし、すべてのパフォーマンスチューニングと同様に、実際のデータに対する実際のテストのみが、特定のクエリに最適なアプローチを教えてくれます。


1

これらの2つの主な違いについて誰も言及しなかったことに驚いています。一時変数は並列挿入をサポートしていますが、テーブル変数はサポートしいません。実行計画との違いを確認できるはずです。そしてこれがChannel 9のSQL Workshopsからのビデオです。

これは、SQLMenaceが以前に回答したように、小さいテーブルに一時変数を使用する必要がある理由、または一時テーブルを使用する理由も説明します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.