小物SEのメモ帳

IT関係の試したことや調べたことの備忘録

【SQL Server】tempdbのCWT_PrimaryKeyとは

実行プランでCWT_PrimaryKeyというテーブルを参照していることがありますが、どのようなものか気になり調べてみました。

これは静的カーソルの結果セットを保存するtempdbのワークテーブルのようです。

静的カーソルは、カーソルを開いた時点の結果をtempdbへ一旦退避し、後続処理ではそれをFETCHして参照することで、カーソル処理中の処理対象への変更有無に関わらず、開いた時点の情報で処理をすることができます。

このときの静的カーソルの結果セットの格納先がtempdbのCWT_PrimaryKeyとなります。

テスト用データ作成

テスト用にテーブルとレコードを作成します。

CREATE DATABASE Cursor_testDB
GO
USE Cursor_testDB
GO

DROP TABLE IF EXISTS dbo.Cursor_test
GO

CREATE TABLE dbo.Cursor_test(
   [id] INT
  ,[column1] VARCHAR(50)
  ,[column2] VARCHAR(50)
  ,CONSTRAINT [PK_column] PRIMARY KEY CLUSTERED ([id])
  )
GO

DECLARE  @Id INT
        ,@Col1 VARCHAR(50)
        ,@Col2 VARCHAR(50)
SET  @id = 1

WHILE (@id < 10000)
  BEGIN
    SET  @Col1 = 'Column' + CONVERT(VARCHAR, @id)
    SET  @Col2 = 'Column' + CONVERT(VARCHAR, 100000-@id)
    INSERT INTO dbo.Cursor_test(id, column1, column2) VALUES (@id, @Col1, @Col2)
    SET @id = @id + 1
  END
GO

作成したテストデータは次のようなものです。
f:id:utiowa:20170721025251p:plain

静的カーソルの確認

静的カーソルでクエリを実行してみます。

DECLARE  @Id INT
        ,@Col1 VARCHAR(50)
        ,@Col2 VARCHAR(50)

DECLARE Static_Cursor CURSOR LOCAL FORWARD_ONLY STATIC READ_ONLY 
  FOR SELECT id,column1,column2
      FROM dbo.Cursor_test
      WHERE id > 9990
      ORDER BY id DESC

OPEN Static_Cursor

FETCH NEXT FROM Static_Cursor INTO @Id,@Col1,@Col2
  WHILE @@FETCH_STATUS = 0
    BEGIN
      PRINT @id
      PRINT @Col1
      PRINT @Col2
      FETCH NEXT FROM Static_Cursor INTO @Id,@Col1,@Col2
    END
  CLOSE Static_Cursor
DEALLOCATE Static_Cursor
GO

f:id:utiowa:20170721025344p:plain

実行プランに表示されている内容からtempdbのCWT_PrimaryKeyというワークテーブルに対して読み取り結果を格納し、その後のFETCH処理はtempdbのCWT_PrimaryKeyからクラスター化インデックスを用いて順次取得していることがわかるかと思います。

複数のクエリで静的カーソルを多用すると一度処理対象となる結果セットはtempdbへ格納され、そこから読出しされるため、tempdbへのIO処理が集中しボトルネックとなることがあります。
また、静的カーソルの対象が多い場合はtempdbへ格納する容量もその分増えることになりますのでどちらも注意が必要かと思います。

【参考情報】
静的カーソル
https://technet.microsoft.com/ja-jp/library/ms191286(v=sql.105).aspx

サーバーカーソル動作とクエリパフォーマンスとの関連性について
https://blogs.msdn.microsoft.com/jpsql/2014/04/29/36899/

DECLARE CURSOR (Transact-SQL)