次の単純なMCVEを考えてみます。
SET STATISTICS IO, TIME OFF;
USE tempdb;
IF OBJECT_ID(N'tempdb..#t1', N'U') IS NOT NULL DROP TABLE #t1;
CREATE TABLE #t1
(
r int NOT NULL
);
IF OBJECT_ID(N'tempdb..##t1', N'U') IS NOT NULL DROP TABLE ##t1;
CREATE TABLE ##t1
(
r int NOT NULL
);
IF OBJECT_ID(N'dbo.s1', N'U') IS NOT NULL DROP TABLE dbo.s1;
CREATE TABLE dbo.s1
(
r int NOT NULL
PRIMARY KEY CLUSTERED
);
INSERT INTO dbo.s1 (r)
SELECT TOP(10000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM sys.syscolumns sc1
CROSS JOIN sys.syscolumns sc2;
GO
次の挿入を実行すると、#t1
に挿入しても、一時テーブルの統計I/Oが表示されません。ただし、##t1
doesに挿入すると、一時テーブルの統計I/Oが表示されます。
SET STATISTICS IO, TIME ON;
GO
INSERT INTO #t1 (r)
SELECT r
FROM dbo.s1;
統計出力:
SQL Serverの解析時間とコンパイル時間: CPU時間= 0ミリ秒、経過時間= 1ミリ秒。 テーブル 's1'。スキャンカウント1、論理読み取り19、物理読み取り0、先読み読み取り0、LOB論理読み取り0、LOB物理読み取り0、LOB先読み読み取り0。 SQL Server実行時間: CPU時間= 16ミリ秒、経過時間= 9ミリ秒。 (10000行に影響)
INSERT INTO ##t1 (r)
SELECT r
FROM dbo.s1;
SQL Serverの解析時間とコンパイル時間: CPU時間= 0ミリ秒、経過時間= 1ミリ秒。 表 '## t1'。スキャンカウント0、論理読み取り10016、物理読み取り0、先読み読み取り0、LOB論理読み取り0、LOB物理読み取り0、LOB先読み読み取り0。 テーブル 's1'。スキャンカウント1、論理読み取り19、物理読み取り0、先読み読み取り0、LOB論理読み取り0、LOB物理読み取り0、LOB先読み読み取り0。 SQL Server実行時間: CPU時間= 47ミリ秒、経過時間= 45ミリ秒。 (10000行に影響)
## tempテーブルに挿入しているだけなのに、なぜ## tempテーブルに非常に多くの読み取りがあるのですか?
_INSERT INTO
_およびグローバル一時テーブルを使用する場合、最小限のロギングは使用されません
_INSERT INTO
_を使用して、グローバル一時テーブルに100万行を挿入する
_INSERT INTO ##t1 (r)
SELECT top(1000000) s1.r
FROM dbo.s1
CROSS APPLY dbo.s1 S2;
_
上記のクエリの実行中にSELECT * FROM fn_dblog(NULL, NULL)
を実行すると、約100万行が返されます。
行ごとに1つの_LOP_INSERT_ROW
_操作+他のログデータ
ローカル一時テーブルへの同じ挿入
_INSERT INTO #t1 (r)
SELECT top(1000000) s1.r
FROM dbo.s1
CROSS APPLY dbo.s1 S2;
_
SELECT * FROM fn_dblog(NULL, NULL)
によって返される行は最大700行まで
最小限のロギング
_SELECT INTO
_を使用してグローバル一時テーブルに100万行を挿入する
_SELECT top(1000000) s1.r
INTO ##t2
FROM dbo.s1
CROSS APPLY dbo.s1 S2;
_
_SELECT INTO
_ 10kレコードのグローバル一時テーブル
_SELECT s1.r
INTO ##t2
FROM dbo.s1;
_
時間とIO統計
_SQL Server parse and compile time:
CPU time = 0 ms, elapsed time = 0 ms.
Table 's1'. Scan count 1, logical reads 19, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 16 ms, elapsed time = 10 ms.
SQL Server parse and compile time:
CPU time = 0 ms, elapsed time = 0 ms.
_
このブログ投稿 に基づいて、ヒープテーブルで最小限のロギングを開始するためにTABLOCK
を追加できます
_INSERT INTO ##t1 WITH(TABLOCK) (r)
SELECT s1.r
FROM dbo.s1
_
低論理読み取り
_Table 's1'. Scan count 1, logical reads 19, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
(10000 rows affected)
_
an answer の一部-@ PaulWhiteによる、一時テーブルでの最小限のロギングを実現する方法
いいえ。ローカル一時テーブル(#temp)は作成セッション専用なので、テーブルロックヒントは必要ありません。 tempdbで作成されたグローバル一時テーブル(## temp)または通常テーブル(dbo.temp)には、複数のセッションからアクセスできるため、テーブルロックヒントが必要です。
これをテストするための通常のテーブルの作成:
_CREATE TABLE dbo.bla
(
r int NOT NULL
);
_
100万レコードで埋めます
_INSERT INTO bla
SELECT top(1000000)s1.r
FROM dbo.s1
CROSS APPLY dbo.s1 S2;
_
>このテーブルで100万回の論理読み取り
_Table 's1'. Scan count 17, logical reads 155, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'bla'. Scan count 0, logical reads 1001607, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
_
Paul Whiteの answer グローバル一時テーブルで報告された論理読み取りの説明
一般に、挿入が最小限のログに記録されていない場合、論理読み取りはターゲットテーブルに対して報告されます。
これらの論理読み取りは、新しい行を追加するための既存の構造内の場所の検索に関連付けられています。最小限のログが記録された挿入では、新しいページ全体またはエクステント全体を割り当てる一括読み込みメカニズムを使用します(したがって、同じ方法でターゲット構造を読み取る必要はありません)。
結論
_INSERT INTO
_は最小限のロギングを使用できないため、グローバル一時テーブル/通常テーブルと組み合わせて使用すると、挿入されたすべての行がtempdbのログファイルに個別にロギングされるという結論になります。一方、ローカル一時テーブル/ _SELECT INTO
_/INSERT INTO ... WITH(TABLOCK)
は、最小限のロギングを使用できます。