tl; dr:CHECKDBがメモリ最適化テーブルを含むユーザーデータベースのトランザクションログを読み取るのはなぜですか?
CHECKDBが、私のデータベースの1つ、特にメモリ内OLTPテーブルを使用するデータベース)をチェックしているときに、ユーザーデータベースのトランザクションログファイルを読み取っているようです。
このデータベースのCHECKDBは、それでも妥当な時間内に終了するため、ほとんどの場合、動作に興味があります。しかし、これはこのインスタンスのすべてのデータベースのCHECKDBの最も長い期間です。
Paul Randalの叙事詩「 CHECKDB From Every Angle:Complete description of all CHECKDB stage、 」を見ると、SQL 2005より前のCHECKDBが使用されていることがわかりますデータベースの一貫したビューを取得するためにログを読み取ります。しかし、これは2016年なので、 内部データベーススナップショットを使用します。
ただし、 snapshots の前提条件の1つは次のとおりです。
ソースデータベースにMEMORY_OPTIMIZED_DATAファイルグループを含めることはできません
ユーザーデータベースにこれらのファイルグループの1つがあるため、スナップショットがテーブルから外れているようです。
CHECKDB docs によると:
スナップショットを作成できない場合、またはTABLOCKが指定されている場合、DBCC CHECKDBはロックを取得して必要な一貫性を確保します。この場合、割り当てチェックを実行するには排他データベースロックが必要であり、テーブルチェックを実行するには共有テーブルロックが必要です。
さて、スナップショットの代わりにデータベースとテーブルのロックを行っています。しかし、それでもトランザクションログを読み取る必要がある理由はわかりません。だから何を与えるのですか?
シナリオを再現するために、以下のスクリプトを用意しました。 _sys.dm_io_virtual_file_stats
_を使用して、ログファイルの読み取りを識別します。
ほとんどの場合、ログのごく一部(480 KB)を読み取りますが、時々それ以上(48.2 MB)を読み取ることに注意してください。私の運用シナリオでは、CHECKDBを実行すると、ほとんどのログファイル(2 GBファイルの約1.3 GB)が毎晩深夜に読み込まれます。
これは、スクリプトでこれまでに得た出力の例です。
_collection_time num_of_reads num_of_bytes_read
2018-04-04 15:12:29.203 106 50545664
_
またはこれ:
_collection_time num_of_reads num_of_bytes_read
2018-04-04 15:25:14.227 1 491520
_
メモリ最適化オブジェクトを通常のテーブルに置き換えると、出力は次のようになります。
_collection_time num_of_reads num_of_bytes_read
2018-04-04 15:21:03.207 0 0
_
CHECKDBがログファイルを読み取るのはなぜですか?特に、ログファイルの大部分が時々読み取られるのはなぜですか?
実際のスクリプトは次のとおりです。
_-- let's have a fresh DB
USE [master];
IF (DB_ID(N'LogFileRead_Test') IS NOT NULL)
BEGIN
ALTER DATABASE [LogFileRead_Test]
SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
DROP DATABASE [LogFileRead_Test];
END
GO
CREATE DATABASE [LogFileRead_Test]
GO
ALTER DATABASE [LogFileRead_Test]
MODIFY FILE
(
NAME = LogFileRead_Test_log,
SIZE = 128MB
);
-- Hekaton-yeah, I want memory optimized data
GO
ALTER DATABASE [LogFileRead_Test]
ADD FILEGROUP [LatencyTestInMemoryFileGroup] CONTAINS MEMORY_OPTIMIZED_DATA;
GO
ALTER DATABASE [LogFileRead_Test]
ADD FILE
(
NAME = [LatencyTestInMemoryFile],
FILENAME = 'C:\Program Files\Microsoft SQL Server\MSSQL13.SQL2016\MSSQL\DATA\LogFileRead_Test_SessionStateInMemoryFile'
) TO FILEGROUP [LatencyTestInMemoryFileGroup];
GO
USE [LogFileRead_Test]
GO
CREATE TYPE [dbo].[InMemoryIdTable] AS TABLE (
[InMemoryId] NVARCHAR (88) COLLATE Latin1_General_100_BIN2 NOT NULL,
PRIMARY KEY NONCLUSTERED HASH ([InMemoryId]) WITH (BUCKET_COUNT = 240))
WITH (MEMORY_OPTIMIZED = ON);
GO
CREATE TABLE [dbo].[InMemoryStuff] (
[InMemoryId] NVARCHAR (88) COLLATE Latin1_General_100_BIN2 NOT NULL,
[Created] DATETIME2 (7) NOT NULL,
CONSTRAINT [PK_InMemoryStuff_InMemoryId] PRIMARY KEY NONCLUSTERED HASH ([InMemoryId]) WITH (BUCKET_COUNT = 240)
)
WITH (MEMORY_OPTIMIZED = ON);
GO
-- RBAR is the new black (we need some logs to read)
declare @j int = 0;
while @j < 100000
begin
INSERT INTO [dbo].[InMemoryStuff](InMemoryId, Created) VALUES ('Description' + CAST(@j as varchar), GETDATE());
set @j = @j + 1;
end
-- grab a baseline of virtual file stats to be diff'd later
select f.num_of_reads, f.num_of_bytes_read
into #dm_io_virtual_file_stats
from sys.dm_io_virtual_file_stats(default, default) f
where database_id = db_id('LogFileRead_Test') and file_id = FILE_IDEX('LogFileRead_Test_log');
-- hands off my log file, CHECKDB!
GO
DBCC CHECKDB ([LogFileRead_Test]) WITH NO_INFOMSGS, ALL_ERRORMSGS, DATA_PURITY;
-- grab the latest virtual file stats, and compare with the previous capture
GO
select f.num_of_reads, f.num_of_bytes_read
into #checkdb_stats
from sys.dm_io_virtual_file_stats(default, default) f
where database_id = db_id('LogFileRead_Test') and file_id = FILE_IDEX('LogFileRead_Test_log');
select
collection_time = GETDATE()
, num_of_reads = - f.num_of_reads + t.num_of_reads
, num_of_bytes_read = - f.num_of_bytes_read + t.num_of_bytes_read
into #dm_io_virtual_file_stats_diff
from #dm_io_virtual_file_stats f, #checkdb_stats t;
drop table #checkdb_stats;
drop table #dm_io_virtual_file_stats;
-- CHECKDB ignored my comment
select collection_time, num_of_reads, num_of_bytes_read
from #dm_io_virtual_file_stats_diff d
order by d.collection_time;
drop table #dm_io_virtual_file_stats_diff;
-- I was *not* raised in a barn
USE [master];
ALTER DATABASE [LogFileRead_Test]
SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
DROP DATABASE [LogFileRead_Test];
_
この再現は通常、1つまたは106個のログファイル読み取りを生成するだけなので、file_readおよびfile_read_completed拡張イベントセッションを使用して1に掘り下げると思いました。
_name timestamp mode offset database_id file_id size duration
file_read 2018-04-06 10:51:11.1098141 Contiguous 72704 9 2 0 NULL
file_read_completed 2018-04-06 10:51:11.1113345 Contiguous 72704 9 2 491520 1
_
そして、これらのオフセットなどのコンテキストのVLF詳細(DBCC LOGINFO()
)です:
_RecoveryUnitId FileId FileSize StartOffset FSeqNo Status Parity CreateLSN
0 2 2031616 8192 34 2 64 0
0 2 2031616 2039808 35 2 64 0
0 2 2031616 4071424 36 2 64 0
0 2 2285568 6103040 37 2 64 0
0 2 15728640 8388608 38 2 64 34000000005200001
0 2 15728640 24117248 39 2 64 34000000005200001
0 2 15728640 39845888 40 2 64 34000000005200001
0 2 15728640 55574528 0 0 0 34000000005200001
0 2 15728640 71303168 0 0 0 34000000005200001
0 2 15728640 87031808 0 0 0 34000000005200001
0 2 15728640 102760448 0 0 0 34000000005200001
0 2 15728640 118489088 0 0 0 34000000005200001
_
したがって、CHECKDB操作:
役立つ場合に備えて、コールスタックもキャプチャしました。
file_readコールスタック:
_(00007ffd`999a0860) sqlmin!XeSqlPkg::file_read::Publish+0x1dc | (00007ffd`999a0b40) sqlmin!XeSqlPkg::file_read_enqueued::Publish
(00007ffd`9a825e30) sqlmin!FireReadEvent+0x118 | (00007ffd`9a825f60) sqlmin!FireReadEnqueuedEvent
(00007ffd`9980b500) sqlmin!FCB::AsyncRead+0x74d | (00007ffd`9980b800) sqlmin!FCB::AsyncReadInternal
(00007ffd`9970e9d0) sqlmin!SQLServerLogMgr::LogBlockReadAheadAsync+0x6a6 | (00007ffd`9970ec00) sqlmin!LBH::Destuff
(00007ffd`9970a6d0) sqlmin!LogConsumer::GetNextLogBlock+0x1591 | (00007ffd`9970ab70) sqlmin!LogPoolPrivateCacheBufferMgr::Lookup
(00007ffd`9a9fcbd0) sqlmin!SQLServerLogIterForward::GetNext+0x258 | (00007ffd`9a9fd2d0) sqlmin!SQLServerLogIterForward::GetNextBlock
(00007ffd`9aa417f0) sqlmin!SQLServerCOWLogIterForward::GetNext+0x2b | (00007ffd`9aa418c0) sqlmin!SQLServerCOWLogIterForward::StartScan
(00007ffd`9aa64210) sqlmin!RecoveryMgr::AnalysisPass+0x83b | (00007ffd`9aa65100) sqlmin!RecoveryMgr::AnalyzeLogRecord
(00007ffd`9aa5ed50) sqlmin!RecoveryMgr::PhysicalRedo+0x233 | (00007ffd`9aa5f790) sqlmin!RecoveryMgr::PhysicalCompletion
(00007ffd`9aa7fd90) sqlmin!RecoveryUnit::PhysicalRecovery+0x358 | (00007ffd`9aa802c0) sqlmin!RecoveryUnit::CompletePhysical
(00007ffd`9a538b90) sqlmin!StartupCoordinator::NotifyPhaseStart+0x3a | (00007ffd`9a538bf0) sqlmin!StartupCoordinator::NotifyPhaseEnd
(00007ffd`9a80c430) sqlmin!DBTABLE::ReplicaCreateStartup+0x2f4 | (00007ffd`9a80c820) sqlmin!DBTABLE::RefreshPostRecovery
(00007ffd`9a7ed0b0) sqlmin!DBMgr::SyncAndLinkReplicaRecoveryPhase+0x890 | (00007ffd`9a7edff0) sqlmin!DBMgr::DetachDB
(00007ffd`9a7f2cd0) sqlmin!DBMgr::CreatePhasedTransientReplica+0x869 | (00007ffd`9a7f3630) sqlmin!DBMgr::StrandTransientReplica
(00007ffd`9a7f2ae0) sqlmin!DBMgr::CreateTransientReplica+0x118 | (00007ffd`9a7f2cd0) sqlmin!DBMgr::CreatePhasedTransientReplica
(00007ffd`99ec6d30) sqlmin!DBDDLAgent::CreateReplica+0x1b5 | (00007ffd`99ec6f90) sqlmin!FSystemDatabase
(00007ffd`9abaaeb0) sqlmin!UtilDbccCreateReplica+0x82 | (00007ffd`9abab000) sqlmin!UtilDbccDestroyReplica
(00007ffd`9ab0d7e0) sqlmin!UtilDbccCheckDatabase+0x994 | (00007ffd`9ab0ffd0) sqlmin!UtilDbccRetainReplica
(00007ffd`9ab0cfc0) sqlmin!DbccCheckDB+0x22d | (00007ffd`9ab0d380) sqlmin!DbccCheckFilegroup
(00007ffd`777379c0) sqllang!DbccCommand::Execute+0x193 | (00007ffd`77737d70) sqllang!DbccHelp
(00007ffd`777e58d0) sqllang!CStmtDbcc::XretExecute+0x889 | (00007ffd`777e6250) sqllang!UtilDbccSetPermissionFailure
(00007ffd`76b02eb0) sqllang!CMsqlExecContext::ExecuteStmts<1,1>+0x40d | (00007ffd`76b03410) sqllang!CSQLSource::CleanupCompileXactState
(00007ffd`76b03a60) sqllang!CMsqlExecContext::FExecute+0xa9e | (00007ffd`76b043d0) sqllang!CCacheObject::Release
(00007ffd`76b03430) sqllang!CSQLSource::Execute+0x981 | (00007ffd`76b039b0) sqllang!CSQLLock::Cleanup
_
file_read_completed呼び出しスタック:
_(00007ffd`99995cc0) sqlmin!XeSqlPkg::file_read_completed::Publish+0x1fc | (00007ffd`99995fe0) sqlmin!XeSqlPkg::file_write_completed::Publish
(00007ffd`9a826630) sqlmin!FireIoCompletionEventLong+0x227 | (00007ffd`9a8269c0) sqlmin!IoRequestDispenser::Dump
(00007ffd`9969bee0) sqlmin!FCB::IoCompletion+0x8e | (00007ffd`9969c180) sqlmin!IoRequestDispenser::Put
(00007ffd`beaa11e0) sqldk!IOQueue::CheckForIOCompletion+0x426 | (00007ffd`beaa1240) sqldk!SystemThread::GetCurrentId
(00007ffd`beaa15b0) sqldk!SOS_Scheduler::SwitchContext+0x173 | (00007ffd`beaa18a0) sqldk!SOS_Scheduler::Switch
(00007ffd`beaa1d00) sqldk!SOS_Scheduler::SuspendNonPreemptive+0xd3 | (00007ffd`beaa1db0) sqldk!SOS_Scheduler::ResumeNoCuzz
(00007ffd`99641720) sqlmin!EventInternal<SuspendQueueSLock>::Wait+0x1e7 | (00007ffd`99641ae0) sqlmin!SOS_DispatcherPool<DispatcherWorkItem,DispatcherWorkItem,SOS_DispatcherQueue<DispatcherWorkItem,0,DispatcherWorkItem>,DispatcherPoolConfig,void * __ptr64>::GetDispatchers
(00007ffd`9aa437c0) sqlmin!SQLServerLogMgr::CheckLogBlockReadComplete+0x1e6 | (00007ffd`9aa44670) sqlmin!SQLServerLogMgr::ValidateBlock
(00007ffd`9970a6d0) sqlmin!LogConsumer::GetNextLogBlock+0x1b37 | (00007ffd`9970ab70) sqlmin!LogPoolPrivateCacheBufferMgr::Lookup
(00007ffd`9a9fcbd0) sqlmin!SQLServerLogIterForward::GetNext+0x258 | (00007ffd`9a9fd2d0) sqlmin!SQLServerLogIterForward::GetNextBlock
(00007ffd`9aa417f0) sqlmin!SQLServerCOWLogIterForward::GetNext+0x2b | (00007ffd`9aa418c0) sqlmin!SQLServerCOWLogIterForward::StartScan
(00007ffd`9aa64210) sqlmin!RecoveryMgr::AnalysisPass+0x83b | (00007ffd`9aa65100) sqlmin!RecoveryMgr::AnalyzeLogRecord
(00007ffd`9aa5ed50) sqlmin!RecoveryMgr::PhysicalRedo+0x233 | (00007ffd`9aa5f790) sqlmin!RecoveryMgr::PhysicalCompletion
(00007ffd`9aa7fd90) sqlmin!RecoveryUnit::PhysicalRecovery+0x358 | (00007ffd`9aa802c0) sqlmin!RecoveryUnit::CompletePhysical
(00007ffd`9a538b90) sqlmin!StartupCoordinator::NotifyPhaseStart+0x3a | (00007ffd`9a538bf0) sqlmin!StartupCoordinator::NotifyPhaseEnd
(00007ffd`9a80c430) sqlmin!DBTABLE::ReplicaCreateStartup+0x2f4 | (00007ffd`9a80c820) sqlmin!DBTABLE::RefreshPostRecovery
(00007ffd`9a7ed0b0) sqlmin!DBMgr::SyncAndLinkReplicaRecoveryPhase+0x890 | (00007ffd`9a7edff0) sqlmin!DBMgr::DetachDB
(00007ffd`9a7f2cd0) sqlmin!DBMgr::CreatePhasedTransientReplica+0x869 | (00007ffd`9a7f3630) sqlmin!DBMgr::StrandTransientReplica
(00007ffd`9a7f2ae0) sqlmin!DBMgr::CreateTransientReplica+0x118 | (00007ffd`9a7f2cd0) sqlmin!DBMgr::CreatePhasedTransientReplica
(00007ffd`99ec6d30) sqlmin!DBDDLAgent::CreateReplica+0x1b5 | (00007ffd`99ec6f90) sqlmin!FSystemDatabase
(00007ffd`9abaaeb0) sqlmin!UtilDbccCreateReplica+0x82 | (00007ffd`9abab000) sqlmin!UtilDbccDestroyReplica
(00007ffd`9ab0d7e0) sqlmin!UtilDbccCheckDatabase+0x994 | (00007ffd`9ab0ffd0) sqlmin!UtilDbccRetainReplica
(00007ffd`9ab0cfc0) sqlmin!DbccCheckDB+0x22d | (00007ffd`9ab0d380) sqlmin!DbccCheckFilegroup
(00007ffd`777379c0) sqllang!DbccCommand::Execute+0x193 | (00007ffd`77737d70) sqllang!DbccHelp
_
これらのスタックトレースは、 Maxの回答 と相関し、CHECKDBがHekatonテーブルが存在するにもかかわらず内部スナップショットを使用していることを示しています。
コミットされていないトランザクションを元に戻すためにスナップショットがリカバリを実行することを読みました :
スナップショットの作成後にデータベースエンジンが回復を実行するため、コミットされていないトランザクションは、新しく作成されたデータベーススナップショットでロールバックされます(データベースのトランザクションは影響を受けません)。
しかし、これでも、ログファイルの大きなチャンクが本番環境のシナリオで(そして場合によってはここで提供される再現で)読み取られることが多い理由を説明していません。私のアプリでは、特定の時間に多くの処理中のトランザクションが発生しているとは思いません。また、ここの再現には何もありません。
SQL Serverのドキュメントでは、「インメモリ」テーブルを含むデータベースはスナップショットをサポートしていないと記載されていますが、checkdb操作はメモリ内テーブルに影響しないため、DBCC CHECKDB
に必要な「内部」スナップショットを作成できます。スナップショットは、ディスク上のテーブルへの変更のみをキャプチャします。
おそらく、Microsoftは、インメモリテーブルを備えたデータベースでユーザーが作成したスナップショットを防止することを選択しました。スナップショットを通常のユーザー中心の感覚からの完全なスナップショットにするために、インメモリー構造を複製する必要があるためです。スナップショットのメモリ内テーブルを複製すると、サーバーのメモリが簡単に不足する可能性がありますnot A Good Thing™
DBCC CHECKDB
の実行中にプライマリデータベースデータファイルが存在するデータフォルダーを監視することで、内部DBCCスナップショットが作成されていることを自分で証明できます。内部スナップショットが作成されると、LogFileRead_Test.mdf_MSSQL_DBCC7
という名前のファイルが表示されます(7
は異なる場合があります-これはデータベースのデータベースIDを表します)。
スナップショットファイルが作成されたら、SQL Serverがデータベースで回復を実行して、DBCC CHECKDBの実行に必要な一貫した状態にする必要があります。表示されているログ読み取りアクションは、その回復プロセスの結果である可能性があります。複数のDBCC CHECKDB
アクションの出力を確認するためのクイックリグを作成しました。これにより、checkdb間にトランザクションがない場合、ログファイルの読み取りが行われないことが証明されます。
USE master;
SET IMPLICIT_TRANSACTIONS OFF;
USE [master];
IF (DB_ID(N'LogFileRead_Test') IS NOT NULL)
BEGIN
ALTER DATABASE [LogFileRead_Test]
SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
DROP DATABASE [LogFileRead_Test];
END
CREATE DATABASE [LogFileRead_Test]
ALTER DATABASE [LogFileRead_Test]
MODIFY FILE
(
NAME = LogFileRead_Test_log,
SIZE = 128MB
);
ALTER DATABASE [LogFileRead_Test]
ADD FILEGROUP [LatencyTestInMemoryFileGroup] CONTAINS MEMORY_OPTIMIZED_DATA;
ALTER DATABASE [LogFileRead_Test]
ADD FILE
(
NAME = [LatencyTestInMemoryFile],
FILENAME = 'C:\temp\LogFileRead_Test_SessionStateInMemoryFile'
) TO FILEGROUP [LatencyTestInMemoryFileGroup];
GO
USE LogFileRead_Test;
CREATE TABLE [dbo].[InMemoryStuff] (
[InMemoryId] NVARCHAR (88) COLLATE Latin1_General_100_BIN2 NOT NULL,
[Created] DATETIME2 (7) NOT NULL,
CONSTRAINT [PK_InMemoryStuff_InMemoryId]
PRIMARY KEY NONCLUSTERED
HASH ([InMemoryId]) WITH (BUCKET_COUNT = 240)
)
WITH (MEMORY_OPTIMIZED = ON);
;WITH src AS (
SELECT n.Num
FROM (VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9))n(Num)
)
INSERT INTO [dbo].[InMemoryStuff] (InMemoryId, Created)
SELECT 'Description' + CONVERT(varchar(30)
, ((s1.Num * 10000)
+ (s2.Num * 1000)
+ (s3.Num * 100)
+ (s4.Num * 10)
+ (s5.Num)))
, GETDATE()
FROM src s1
CROSS JOIN src s2
CROSS JOIN src s3
CROSS JOIN src s4
CROSS JOIN src s5;
USE master;
DECLARE @cmd nvarchar(max);
DECLARE @msg nvarchar(1000);
DECLARE @l int;
DECLARE @m int;
SET @m = 10;
SET @l = 1;
IF OBJECT_ID(N'tempdb..#vfs', N'U') IS NOT NULL DROP TABLE #vfs;
CREATE TABLE #vfs (
vfs_run int NOT NULL IDENTITY(1,1) PRIMARY KEY CLUSTERED
, collection_time datetime2(7)
, num_of_reads bigint
, num_of_bytes_read bigint
);
WHILE @l <= @m
BEGIN
SET @msg = N'loop ' + CONVERT(nvarchar(10), @l);
RAISERROR (@msg, 0, 1) WITH NOWAIT;
SET @cmd = 'USE [LogFileRead_Test];
-- grab a baseline of virtual file stats to be diff''d later
select f.num_of_reads, f.num_of_bytes_read
into #dm_io_virtual_file_stats
from sys.dm_io_virtual_file_stats(default, default) f
where database_id = db_id(''LogFileRead_Test'') and file_id = FILE_IDEX(''LogFileRead_Test_log'');
DBCC CHECKDB ([LogFileRead_Test]) WITH NO_INFOMSGS, ALL_ERRORMSGS, DATA_PURITY;
-- grab the latest virtual file stats, and compare with the previous capture
select f.num_of_reads, f.num_of_bytes_read
into #checkdb_stats
from sys.dm_io_virtual_file_stats(default, default) f
where database_id = db_id(''LogFileRead_Test'') and file_id = FILE_IDEX(''LogFileRead_Test_log'');
select
collection_time = GETDATE()
, num_of_reads = - f.num_of_reads + t.num_of_reads
, num_of_bytes_read = - f.num_of_bytes_read + t.num_of_bytes_read
into #dm_io_virtual_file_stats_diff
from #dm_io_virtual_file_stats f, #checkdb_stats t;
--drop table #checkdb_stats;
--drop table #dm_io_virtual_file_stats;
-- CHECKDB ignored my comment
select collection_time, num_of_reads, num_of_bytes_read
from #dm_io_virtual_file_stats_diff d
order by d.collection_time;
--drop table #dm_io_virtual_file_stats_diff;
';
INSERT INTO #vfs (collection_time, num_of_reads, num_of_bytes_read)
EXEC sys.sp_executesql @cmd;
SET @l += 1;
END
USE master;
SET @cmd = 'USE [master];
ALTER DATABASE [LogFileRead_Test]
SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
DROP DATABASE [LogFileRead_Test];
';
EXEC sys.sp_executesql @cmd;
SELECT *
FROM #vfs
ORDER BY vfs_run;
結果:
╔═════════╦═════════════════════════════╦═════════ ═════╦═══════════════════╗ ║vfs_run║collection_time║num_of_reads║num_of_bytes_read║ ╠════ ═════╬═════════════════════════════╬══════════════ ╬═══════════════════╣ ║1║2018-04-06 15:53:37.6566667║1║491520║ ║ 2║2018-04-06 15:53:37.8300000║0║0║ ║3║2018-04-06 15:53:38.0166667║0║0║ ║4║2018- 04-06 15:53:38.1866667║0║0║ ║5║2018-04-06 15:53:38.3766667║0║0║ ║6║2018-04-06 15 :53:38.5633333║0║0║ ║7║2018-04-06 15:53:38.7333333║0║0║ ║8║2018-04-06 15:53:38.9066667 ║0║0║ ║9║2018-04-06 15:53:39.0933333║0║0║ ║10║2018-04-06 15:53:39.2800000║0║0║ ╚═════════╩═══════════ ══════════════════╩══════════════╩════════════════ ═══╝
また、データをテストテーブルに挿入するためにRBARアプローチを使用する代わりに、以下のような単純なセットベースのアプローチを使用することもできます。
;WITH src AS (
SELECT n.Num
FROM (VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9))n(Num)
)
INSERT INTO [dbo].[InMemoryStuff] (InMemoryId, Created)
SELECT 'Description' + CONVERT(varchar(30)
, ((s1.Num * 10000)
+ (s2.Num * 1000)
+ (s3.Num * 100)
+ (s4.Num * 10)
+ (s5.Num)))
, GETDATE()
FROM src s1
CROSS JOIN src s2
CROSS JOIN src s3
CROSS JOIN src s4
CROSS JOIN src s5;
私のテストでは、3秒未満でテーブルがいっぱいになりましたが、RBARアプローチは長い時間を要します。また、あなたのコードの素敵なコメントは私を笑わせました。