回答:
内容
警告
この回答では、SQL Server 2000で導入された「クラシック」テーブル変数について説明します。メモリOLTPのSQL Server 2014では、メモリ最適化テーブルタイプが導入されます。それらのテーブル変数インスタンスは、以下で説明するものと多くの点で異なります!(詳細)。
ストレージの場所
変わりはない。両方ともに保存されtempdb
ます。
テーブル変数の場合、これは常に当てはまるわけではないが、以下から確認できることが示唆されています。
DECLARE @T TABLE(X INT)
INSERT INTO @T VALUES(1),(2)
SELECT sys.fn_PhysLocFormatter(%%physloc%%) AS [File:Page:Slot]
FROM @T
結果の例(tempdb
2行の場所が表示されます)
File:Page:Slot
----------------
(1:148:0)
(1:148:1)
論理的な場所
@table_variables
#temp
テーブルよりも現在のデータベースの一部であるかのように振る舞います。テーブル変数(2005年以降)の列照合は、明示的に指定されていない場合、現在のデータベースの#temp
照合順序になりますが、テーブルの場合、デフォルト照合順序tempdb
(詳細)が使用されます。さらに、#temp
テーブルに使用するユーザー定義のデータ型とXMLコレクションはtempdbになければなりませんが、テーブル変数は現在のデータベース(ソース)からそれらを使用できます。
SQL Server 2012では、包含データベースが導入されています。これらの一時テーブルの動作は異なります(h / tアーロン)
包含データベースでは、一時テーブルのデータは包含データベースの照合で照合されます。
- 一時テーブルに関連付けられているすべてのメタデータ(テーブルと列の名前、インデックスなど)はカタログ照合に含まれます。
- 名前付き制約は、一時テーブルでは使用できません。
- 一時テーブルは、ユーザー定義型、XMLスキーマコレクション、またはユーザー定義関数を参照できません。
異なるスコープへの可視性
@table_variables
宣言されているバッチとスコープ内でのみアクセスできます。#temp_tables
子バッチ内でアクセス可能です(ネストされたトリガー、プロシージャー、exec
呼び出し)。#temp_tables
外部スコープ(@@NESTLEVEL=0
)で作成されたセッションは、セッションが終了するまで持続するため、バッチにまたがることができます。ただし、次に説明するように、どちらのタイプのオブジェクトも子バッチで作成し、呼び出しスコープでアクセスすることはできません(グローバル##temp
テーブルでも可能です)。
一生
@table_variables
DECLARE @.. TABLE
ステートメントを含むバッチが実行されると(そのバッチのユーザーコードが実行される前に)暗黙的に作成され、最後に暗黙的にドロップされます。
パーサーでは、DECLARE
ステートメントの前にテーブル変数を試して使用することはできませんが、暗黙的な作成を以下に示します。
IF (1 = 0)
BEGIN
DECLARE @T TABLE(X INT)
END
--Works fine
SELECT *
FROM @T
#temp_tables
TSQL CREATE TABLE
ステートメントが検出されたときに明示的に作成され、明示的にドロップできるDROP TABLE
か、バッチが終了したときに暗黙的にドロップされます(で子バッチで作成された場合@@NESTLEVEL > 0
)またはそうでない場合はセッションが終了します。
NBは:ストアドルーチン内のオブジェクトの両方のタイプをキャッシュすることができ、繰り返し作成し、新しいテーブルを落とすのではなく。以下のために違反する可能性がある。このキャッシュはしかし起こることができるときには制限があります#temp_tables
が、上の制約がどの@table_variables
とにかく防ぐには。ここに示すように、キャッシュ#temp
テーブルのメンテナンスオーバーヘッドは、テーブル変数の場合よりもわずかに大きくなります。
オブジェクトのメタデータ
これは、両方のタイプのオブジェクトで本質的に同じです。のシステムベーステーブルに保存されtempdb
ます。#temp
ただしOBJECT_ID('tempdb..#T')
、システムテーブルにキー入力するために使用できるため、テーブル を確認する方が簡単です。また、内部で生成された名前は、CREATE TABLE
ステートメントで定義された名前により密接に関連付けられます。テーブル変数の場合、object_id
関数は機能せず、内部名はシステムによって完全に生成され、変数名とは関係ありません。以下は、メタデータがまだ存在することを示しています(ただし、一意であることが望ましい)列名を入力します。一意の列名を持たないテーブルの場合、object_id DBCC PAGE
は空でない限り使用して決定できます。
/*Declare a table variable with some unusual options.*/
DECLARE @T TABLE
(
[dba.se] INT IDENTITY PRIMARY KEY NONCLUSTERED,
A INT CHECK (A > 0),
B INT DEFAULT 1,
InRowFiller char(1000) DEFAULT REPLICATE('A',1000),
OffRowFiller varchar(8000) DEFAULT REPLICATE('B',8000),
LOBFiller varchar(max) DEFAULT REPLICATE(cast('C' as varchar(max)),10000),
UNIQUE CLUSTERED (A,B)
WITH (FILLFACTOR = 80,
IGNORE_DUP_KEY = ON,
DATA_COMPRESSION = PAGE,
ALLOW_ROW_LOCKS=ON,
ALLOW_PAGE_LOCKS=ON)
)
INSERT INTO @T (A)
VALUES (1),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13)
SELECT t.object_id,
t.name,
p.rows,
a.type_desc,
a.total_pages,
a.used_pages,
a.data_pages,
p.data_compression_desc
FROM tempdb.sys.partitions AS p
INNER JOIN tempdb.sys.system_internals_allocation_units AS a
ON p.hobt_id = a.container_id
INNER JOIN tempdb.sys.tables AS t
ON t.object_id = p.object_id
INNER JOIN tempdb.sys.columns AS c
ON c.object_id = p.object_id
WHERE c.name = 'dba.se'
出力
Duplicate key was ignored.
+-----------+-----------+------+-------------------+-------------+------------+------------+-----------------------+
| object_id | name | rows | type_desc | total_pages | used_pages | data_pages | data_compression_desc |
+-----------+-----------+------+-------------------+-------------+------------+------------+-----------------------+
| 574625090 | #22401542 | 13 | IN_ROW_DATA | 2 | 2 | 1 | PAGE |
| 574625090 | #22401542 | 13 | LOB_DATA | 24 | 19 | 0 | PAGE |
| 574625090 | #22401542 | 13 | ROW_OVERFLOW_DATA | 16 | 14 | 0 | PAGE |
| 574625090 | #22401542 | 13 | IN_ROW_DATA | 2 | 2 | 1 | NONE |
+-----------+-----------+------+-------------------+-------------+------------+------------+-----------------------+
取引
上の操作@table_variables
は、外部ユーザートランザクションとは無関係にシステムトランザクションとして実行されますが、同等の#temp
テーブル操作はユーザートランザクション自体の一部として実行されます。このため、ROLLBACK
コマンドは#temp
テーブルに影響しますが、@table_variable
そのまま残ります。
DECLARE @T TABLE(X INT)
CREATE TABLE #T(X INT)
BEGIN TRAN
INSERT #T
OUTPUT INSERTED.X INTO @T
VALUES(1),(2),(3)
/*Both have 3 rows*/
SELECT * FROM #T
SELECT * FROM @T
ROLLBACK
/*Only table variable now has rows*/
SELECT * FROM #T
SELECT * FROM @T
DROP TABLE #T
ロギング
どちらもtempdb
トランザクションログにログレコードを生成します。一般的な誤解は、これはテーブル変数の場合ではないため、これを示すスクリプトは以下のとおりです。テーブル変数を宣言し、いくつかの行を追加してから更新および削除します。
テーブル変数はバッチの開始時と終了時に暗黙的に作成および削除されるため、完全なログを表示するには複数のバッチを使用する必要があります。
USE tempdb;
/*
Don't run this on a busy server.
Ideally should be no concurrent activity at all
*/
CHECKPOINT;
GO
/*
The 2nd column is binary to allow easier correlation with log output shown later*/
DECLARE @T TABLE ([C71ACF0B-47E9-4CAD-9A1E-0C687A8F9CF3] INT, B BINARY(10))
INSERT INTO @T
VALUES (1, 0x41414141414141414141),
(2, 0x41414141414141414141)
UPDATE @T
SET B = 0x42424242424242424242
DELETE FROM @T
/*Put allocation_unit_id into CONTEXT_INFO to access in next batch*/
DECLARE @allocId BIGINT, @Context_Info VARBINARY(128)
SELECT @Context_Info = allocation_unit_id,
@allocId = a.allocation_unit_id
FROM sys.system_internals_allocation_units a
INNER JOIN sys.partitions p
ON p.hobt_id = a.container_id
INNER JOIN sys.columns c
ON c.object_id = p.object_id
WHERE ( c.name = 'C71ACF0B-47E9-4CAD-9A1E-0C687A8F9CF3' )
SET CONTEXT_INFO @Context_Info
/*Check log for records related to modifications of table variable itself*/
SELECT Operation,
Context,
AllocUnitName,
[RowLog Contents 0],
[Log Record Length]
FROM fn_dblog(NULL, NULL)
WHERE AllocUnitId = @allocId
GO
/*Check total log usage including updates against system tables*/
DECLARE @allocId BIGINT = CAST(CONTEXT_INFO() AS BINARY(8));
WITH T
AS (SELECT Operation,
Context,
CASE
WHEN AllocUnitId = @allocId THEN 'Table Variable'
WHEN AllocUnitName LIKE 'sys.%' THEN 'System Base Table'
ELSE AllocUnitName
END AS AllocUnitName,
[Log Record Length]
FROM fn_dblog(NULL, NULL) AS D)
SELECT Operation = CASE
WHEN GROUPING(Operation) = 1 THEN 'Total'
ELSE Operation
END,
Context,
AllocUnitName,
[Size in Bytes] = COALESCE(SUM([Log Record Length]), 0),
Cnt = COUNT(*)
FROM T
GROUP BY GROUPING SETS( ( Operation, Context, AllocUnitName ), ( ) )
ORDER BY GROUPING(Operation),
AllocUnitName
返却値
私が両方の操作を識別できる限り、ほぼ同じ量のロギングが生成されます。
ロギングの量は非常に似ていますが、1つの重要な違いは、#temp
ユーザートランザクションを含むものが終了するまでテーブルに関連するログレコードを消去できないため、ある時点で#temp
テーブルに書き込む長時間実行トランザクションはログの切り捨てを防ぎますtempdb
が、自律型トランザクションはテーブル変数のために生成されません。
テーブル変数はサポートしTRUNCATE
ていないため、テーブルからすべての行を削除することが要件である場合、ロギングのデメリットになる可能性があります(ただし、非常に小さなテーブルの場合DELETE
はいずれにせようまくいきます)
カーディナリティ
テーブル変数を含む実行計画の多くは、それらからの出力として推定される単一の行を表示します。テーブル変数のプロパティを調べると、SQL Serverがテーブル変数の行がゼロであると認識していることがわかります(ゼロ行のテーブルから1行が出力されると推定される理由は、@ Paul Whiteによって説明されています)。
ただし、前のセクションで示した結果は、で正確なrows
カウントを示していますsys.partitions
。問題は、ほとんどの場合、テーブルが空である間にテーブル変数を参照するステートメントがコンパイルされることです。ステートメントが@table_variable
移入された後にステートメントが(再)コンパイルされる場合、これは代わりにテーブルのカーディナリティに使用されます(これは、明示的recompile
またはおそらくステートメントが遅延コンパイルまたは再コンパイルを引き起こす別のオブジェクトを参照するために発生する可能性があります)
DECLARE @T TABLE(I INT);
INSERT INTO @T VALUES(1),(2),(3),(4),(5)
CREATE TABLE #T(I INT)
/*Reference to #T means this statement is subject to deferred compile*/
SELECT * FROM @T WHERE NOT EXISTS(SELECT * FROM #T)
DROP TABLE #T
計画は、遅延コンパイル後の正確な推定行数を示します。
SQL Server 2012 SP2では、トレースフラグ2453が導入されています。詳細については、「リレーショナルエンジン」を参照してください。
このトレースフラグを有効にすると、自動再コンパイルが発生するカーディナリティが考慮されるようになります。
注意:互換性レベル150のAzureでは、ステートメントのコンパイルは最初の実行まで延期されます。これは、ゼロ行の推定問題の影響を受けなくなることを意味します。
列統計なし
ただし、テーブルのカーディナリティがより正確であっても、推定行数がより正確になるわけではありません(テーブル内のすべての行で操作を実行する場合を除く)。SQL Serverはテーブル変数の列統計をまったく維持しないため、比較述語に基づいた推測に基づいてフォールバックします(たとえば、テーブルの10%が=
一意でない列に対して返され、30%が>
比較に対して返されます)。対照的に、テーブルの列統計は維持され#temp
ます。
SQL Serverは、各列に加えられた変更の数のカウントを保持します。計画がコンパイルされてからの変更の数が再コンパイルのしきい値(RT)を超える場合、計画は再コンパイルされ、統計が更新されます。RTはテーブルのタイプとサイズに依存します。
RTは次のように計算されます。(nは、クエリプランがコンパイルされるときのテーブルのカーディナリティを指します。)
永久表
-n <= 500の場合、RT =500。
-n> 500の場合、RT = 500 + 0.20 * n。一時テーブル
-n <6の場合、RT = 6-6
<= n <= 500の場合、RT =500。
-n> 500の場合、RT = 500 + 0.20 * n。
テーブル変数
-RTは存在しません。したがって、テーブル変数のカーディナリティが変更されているため、再コンパイルは行われません。 (ただし、以下のTF 2453に関する注意を参照してください)
KEEP PLAN
ヒントは、室温を設定するために使用することができる#temp
永続テーブルと同じテーブル。
このすべての最終的な効果は、#temp
テーブルに対して生成される実行プラン@table_variables
は、SQL Serverがより適切な情報を処理するため、多くの行が含まれる場合よりも桁違いに優れていることです。
NB1:テーブル変数には統計はありませんが、トレースフラグ2453の下で「統計変更」再コンパイルイベントが発生する可能性があります(「単純な」プランには適用されません)。追加の場合N=0 -> RT = 1
。つまり、テーブル変数が空のときにコンパイルされたすべてのステートメントは、空でないTableCardinality
ときに最初に実行されたときに再コンパイルおよび修正されます。コンパイル時テーブルのカーディナリティはプランに格納され、同じカーディナリティでステートメントが再度実行された場合(制御ステートメントのフローまたはキャッシュされたプランの再利用のいずれか)、再コンパイルは発生しません。
NB2:ストアドプロシージャのキャッシュされた一時テーブルの場合、再コンパイルのストーリーは上記の説明よりもはるかに複雑です。参照してください。ストアドプロシージャ内の一時テーブルのすべての血みどろの詳細については、を。
再コンパイル
同様に、上述の変形ベースの再コンパイル#temp
テーブルも関連付けることができる追加のコンパイル彼らはコンパイル(例えばDDL変更をトリガするテーブル変数のために禁止されている操作を許可単にのでCREATE INDEX
、ALTER TABLE
)
ロッキング
述べられているテーブル変数は、ロックに参加しないこと。これはそうではありません。以下を実行すると、SSMSメッセージタブに、挿入ステートメントで取得および解放されたロックの詳細が出力されます。
DECLARE @tv_target TABLE (c11 int, c22 char(100))
DBCC TRACEON(1200,-1,3604)
INSERT INTO @tv_target (c11, c22)
VALUES (1, REPLICATE('A',100)), (2, REPLICATE('A',100))
DBCC TRACEOFF(1200,-1,3604)
SELECT
テーブル変数からのクエリの場合、Paul Whiteは、暗黙的にNOLOCK
ヒントが自動的に付けられることをコメントで指摘しています。これを以下に示します
DECLARE @T TABLE(X INT);
SELECT X
FROM @T
OPTION (RECOMPILE, QUERYTRACEON 3604, QUERYTRACEON 8607)
*** Output Tree: (trivial plan) ***
PhyOp_TableScan TBL: @T Bmk ( Bmk1000) IsRow: COL: IsBaseRow1002 Hints( NOLOCK )
ただし、これがロックに与える影響はごくわずかです。
SET NOCOUNT ON;
CREATE TABLE #T( [ID] [int] IDENTITY NOT NULL,
[Filler] [char](8000) NULL,
PRIMARY KEY CLUSTERED ([ID] DESC))
DECLARE @T TABLE ( [ID] [int] IDENTITY NOT NULL,
[Filler] [char](8000) NULL,
PRIMARY KEY CLUSTERED ([ID] DESC))
DECLARE @I INT = 0
WHILE (@I < 10000)
BEGIN
INSERT INTO #T DEFAULT VALUES
INSERT INTO @T DEFAULT VALUES
SET @I += 1
END
/*Run once so compilation output doesn't appear in lock output*/
EXEC('SELECT *, sys.fn_PhysLocFormatter(%%physloc%%) FROM #T')
DBCC TRACEON(1200,3604,-1)
SELECT *, sys.fn_PhysLocFormatter(%%physloc%%)
FROM @T
PRINT '--*--'
EXEC('SELECT *, sys.fn_PhysLocFormatter(%%physloc%%) FROM #T')
DBCC TRACEOFF(1200,3604,-1)
DROP TABLE #T
これらのどちらも、SQL Server が両方に対して割り当て順序スキャンを使用したことを示すインデックスキーの順序になりません。
上記のスクリプトを2回実行し、2回目の実行の結果は以下のとおりです。
Process 58 acquiring Sch-S lock on OBJECT: 2:-1325894110:0 (class bit0 ref1) result: OK
--*--
Process 58 acquiring IS lock on OBJECT: 2:-1293893996:0 (class bit0 ref1) result: OK
Process 58 acquiring S lock on OBJECT: 2:-1293893996:0 (class bit0 ref1) result: OK
Process 58 releasing lock on OBJECT: 2:-1293893996:0
SQL Serverはオブジェクトのスキーマ安定性ロックを取得するだけなので、テーブル変数のロック出力は非常に最小限です。しかし、#temp
テーブルの場合は、オブジェクトレベルのS
ロックを解除するという点でほぼ同じです。NOLOCK
ヒントまたはREAD UNCOMMITTED
で作業するとき分離レベルは、もちろん明示的に指定することができる#temp
だけでなくテーブル。
周囲のユーザートランザクションのログに関する問題と同様に、#temp
テーブルのロックが長く保持されることを意味します。以下のスクリプトで
--BEGIN TRAN;
CREATE TABLE #T (X INT,Y CHAR(4000) NULL);
INSERT INTO #T (X) VALUES(1)
SELECT CASE resource_type
WHEN 'OBJECT' THEN OBJECT_NAME(resource_associated_entity_id, 2)
WHEN 'ALLOCATION_UNIT' THEN (SELECT OBJECT_NAME(object_id, 2)
FROM tempdb.sys.allocation_units a
JOIN tempdb.sys.partitions p ON a.container_id = p.hobt_id
WHERE a.allocation_unit_id = resource_associated_entity_id)
WHEN 'DATABASE' THEN DB_NAME(resource_database_id)
ELSE (SELECT OBJECT_NAME(object_id, 2)
FROM tempdb.sys.partitions
WHERE partition_id = resource_associated_entity_id)
END AS object_name,
*
FROM sys.dm_tran_locks
WHERE request_session_id = @@SPID
DROP TABLE #T
-- ROLLBACK
どちらの場合も明示的なユーザートランザクションの外部で実行された場合、チェック時に返されるロックsys.dm_tran_locks
はの共有ロックのみDATABASE
です。
コメントを解除すると、BEGIN TRAN ... ROLLBACK
26行が返され、オブジェクト自体とシステムテーブル行の両方でロックが保持され、ロールバックが可能になり、他のトランザクションがコミットされていないデータを読み取れないようになります。同等のテーブル変数操作は、ユーザートランザクションによるロールバックの対象ではなく、次のステートメントでチェックするためにこれらのロックを保持する必要はありませんが、プロファイラーで取得およびリリースされたロックをトレースするか、トレースフラグ1200を使用すると、多くのロックイベントがまだ表示されます発生する。
インデックス
SQL Server 2014より前のバージョンでは、一意の制約または主キーを追加する副作用として、テーブル変数にのみ暗黙的にインデックスを作成できます。これはもちろん、一意のインデックスのみがサポートされることを意味します。独自のクラスタ化インデックスを持つテーブル上の非ユニークな非クラスタ化インデックスは、単にそれを宣言することがシミュレートすることができますUNIQUE NONCLUSTERED
(SQL Serverがなり、希望NCIキーの最後にCIキーを追加し、とにかく舞台裏でこれを行うにも非ユニーク場合NCIを指定できます)
実証され、以前の様々なとしてindex_option
sが含む制約宣言で指定することができDATA_COMPRESSION
、IGNORE_DUP_KEY
とFILLFACTOR
(それが唯一のインデックス上の任意の差が再構築になるだろうとあなたは、テーブル変数にインデックスを再構築することができないように、その1を設定するにはポイントがないのに!)
さらに、テーブル変数はINCLUDE
d列、フィルター選択されたインデックス(2016年まで)、またはパーティション分割をサポートしていませ#temp
ん(テーブルでパーティション分割スキームを作成する必要がありますtempdb
)。
SQL Server 2014のインデックス
非一意インデックスは、SQL Server 2014のテーブル変数定義でインラインで宣言できます。これの構文の例を以下に示します。
DECLARE @T TABLE (
C1 INT INDEX IX1 CLUSTERED, /*Single column indexes can be declared next to the column*/
C2 INT INDEX IX2 NONCLUSTERED,
INDEX IX3 NONCLUSTERED(C1,C2) /*Example composite index*/
);
SQL Server 2016のインデックス
CTP 3.1から、テーブル変数のフィルター選択されたインデックスを宣言できるようになりました。RTMによってそれがあり、彼らはいえ列も許可されている場合が含ま可能性が高いため、リソースの制約にSQL16にそれをすることはありません
DECLARE @T TABLE
(
c1 INT NULL INDEX ix UNIQUE WHERE c1 IS NOT NULL /*Unique ignoring nulls*/
)
平行度
(または変更)に挿入クエリが@table_variables
並列プランを持つことができない、#temp_tables
このように限定されるものではありません。
次のように書き直すことでSELECT
パーツを並行して実行できるようになりますが、最終的には(舞台裏で)隠された一時テーブルを使用するという明らかな回避策があります。
INSERT INTO @DATA ( ... )
EXEC('SELECT .. FROM ...')
ここでの私の答えに示されているように、テーブル変数から選択するクエリにはそのような制限はありません
その他の機能の違い
#temp_tables
関数内で使用することはできません。@table_variables
スカラーまたはマルチステートメントテーブルUDF内で使用できます。@table_variables
名前付き制約を持つことはできません。@table_variables
することはできませんSELECT
-ed INTO
、ALTER
、-ed TRUNCATE
Dまたはの対象となるDBCC
ようなコマンドDBCC CHECKIDENT
やのSET IDENTITY INSERT
やなどのテーブルヒントをサポートしていません。WITH (FORCESCAN)
CHECK
テーブル変数の制約は、単純化、暗黙の述部、または矛盾検出のためにオプティマイザーによって考慮されません。PAGELATCH_EX
待機に直面する可能性があります。(例)メモリのみ?
冒頭で述べたように、両方とものページに保存されますtempdb
。ただし、これらのページをディスクに書き込む際の動作に違いがあるかどうかについては触れませんでした。
私は今、これについて少量のテストを行ってきましたが、今のところそのような違いは見られません。SQL Server 250ページのインスタンスで行った特定のテストでは、データファイルが書き込まれる前のカットオフポイントのようです。
注意:以下の動作は、SQL Server 2014またはSQL Server 2012 SP1 / CU10またはSP2 / CU1では発生しなくなりました。SQL Server 2014でのその変更の詳細:tempdb Hidden Performance Gem。
以下のスクリプトを実行する
CREATE TABLE #T(X INT, Filler char(8000) NULL)
INSERT INTO #T(X)
SELECT TOP 250 ROW_NUMBER() OVER (ORDER BY @@SPID)
FROM master..spt_values
DROP TABLE #T
またtempdb
、Process Monitorを使用したデータファイルへの書き込みの監視では、何も表示されませんでした(オフセット73,728でのデータベースブートページへの書き込みを除くことがあります)。に変更250
した後、251
私は以下のように書き込みを見始めました。
上記のスクリーンショットは、5 * 32ページの書き込みと、161ページがディスクに書き込まれたことを示す1ページの書き込みを示しています。テーブル変数を使用したテストでも、250ページという同じカットオフポイントを取得しました。以下のスクリプトは、別の方法を示しています。sys.dm_os_buffer_descriptors
DECLARE @T TABLE (
X INT,
[dba.se] CHAR(8000) NULL)
INSERT INTO @T
(X)
SELECT TOP 251 Row_number() OVER (ORDER BY (SELECT 0))
FROM master..spt_values
SELECT is_modified,
Count(*) AS page_count
FROM sys.dm_os_buffer_descriptors
WHERE database_id = 2
AND allocation_unit_id = (SELECT a.allocation_unit_id
FROM tempdb.sys.partitions AS p
INNER JOIN tempdb.sys.system_internals_allocation_units AS a
ON p.hobt_id = a.container_id
INNER JOIN tempdb.sys.columns AS c
ON c.object_id = p.object_id
WHERE c.name = 'dba.se')
GROUP BY is_modified
is_modified page_count
----------- -----------
0 192
1 61
192ページがディスクに書き込まれ、ダーティフラグがクリアされたことを示しています。また、ディスクに書き込まれたからといって、ページがバッファプールからすぐに削除されるわけではないことも示しています。このテーブル変数に対するクエリは、メモリから完全に満たすことができます。
約1,843,000 KB(約23,000ページ)として割り当てられたバッファプールページにmax server memory
設定2000 MB
およびDBCC MEMORYSTATUS
報告するアイドルサーバーで、1,000行/ページのバッチで上記のテーブルに挿入し、各反復について記録しました。
SELECT Count(*)
FROM sys.dm_os_buffer_descriptors
WHERE database_id = 2
AND allocation_unit_id = @allocId
AND page_type = 'DATA_PAGE'
テーブル変数とテーブルの両方#temp
がほぼ同一のグラフを提供し、それらがメモリに完全に保持されていない点に到達する前にバッファプールをほぼ最大限に管理したため、メモリの量に特別な制限はないようですどちらも消費できます。
勉強よりも特定の経験に基づいて指摘したいことがいくつかあります。DBAとして、私は非常に新しいので、必要に応じて修正してください。