# 通知設計書 85-カーソルプラン調査

## 概要

本ドキュメントは、SQL Server First Responder Kitのsp_BlitzCache.sqlにおける「カーソルプラン調査」通知の設計を定義する。この通知は、クエリプランキャッシュの分析中にカーソルプランが検出された場合に、カーソルの詳細な調査を開始することを示す情報メッセージである。

### 本通知の処理概要

この通知は、sp_BlitzCacheストアドプロシージャがキャッシュされた実行プランを分析する際に、カーソルを使用するクエリプランが検出された場合に、カーソルの詳細情報（楽観的カーソル、前方専用カーソルなど）を調査する処理の開始を示す。

**業務上の目的・背景**：カーソルはSQL Serverにおいてパフォーマンスに重大な影響を与える可能性がある構造である。行単位の処理を行うカーソルは、セットベースの処理と比較して著しく遅くなることが多い。また、カーソルの種類（静的、動的、キーセット、前方専用）や並行性モード（読み取り専用、楽観的、スクロールロック）によってパフォーマンス特性が異なる。この通知により、カーソルプランが検出され調査が開始されたことをユーザーに通知し、分析結果を待つよう促す。

**通知の送信タイミング**：sp_BlitzCacheプロシージャの実行中、#statementsテーブルにis_cursor = 1のレコードが存在する場合に、カーソルプラン調査処理が開始される直前のタイミングで送信される。カーソルプランが存在しない場合は「No cursor plans found, skipping」というメッセージが出力され、本通知は送信されない。

**通知の受信者**：この通知はRAISERROR関数によりSQL Server Management Studio（SSMS）のメッセージウィンドウ、またはsqlcmdなどのコマンドラインツールの標準出力に表示される。主な受信者は、sp_BlitzCacheを実行しているDBA、データベース開発者、システム管理者である。

**通知内容の概要**：「Cursor plans found, investigating」という英語メッセージが表示され、カーソルプランが検出されて調査を開始することを示す。これにより、追加の分析処理が実行されることを把握できる。

**期待されるアクション**：通知の受信者は、カーソルプランの調査が開始されたことを認識し、処理完了を待つ。分析結果においてカーソルが検出された場合は、セットベースの処理への書き換えを検討するか、より効率的なカーソルオプション（FAST_FORWARDなど）の使用を検討する。

## 通知種別

コンソール出力（RAISERROR WITH NOWAIT）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（WITH NOWAITオプションにより即座に出力） |
| 優先度 | 低（情報レベル、重大度0） |
| リトライ | なし（単発の情報メッセージ） |

### 送信先決定ロジック

sp_BlitzCacheを実行しているセッションのクライアント接続に対して直接出力される。送信先の動的な決定ロジックは存在せず、実行元のセッションに対して常に通知される。

## 通知テンプレート

### メール通知の場合

該当なし（コンソール出力のみ）

### 本文テンプレート

```
Cursor plans found, investigating
```

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| なし | 固定メッセージのため変数なし | - | - |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| プロシージャ実行 | sp_BlitzCache内のカーソルプラン調査開始 | #statementsにis_cursor = 1のレコードが存在 | カーソルプランが検出された場合のみ通知 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| カーソルプランなし | #statementsにis_cursor = 1のレコードが存在しない場合は「No cursor plans found, skipping」を出力 |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[ソートコストチェック完了] --> B{#statementsにカーソル存在?}
    B -->|No| C[No cursor plans found出力]
    B -->|Yes| D[RAISERROR実行]
    C --> I[次のチェック処理へ]
    D --> E[メッセージ即時出力]
    E --> F[楽観的カーソルチェック]
    F --> G[前方専用カーソルチェック]
    G --> H[カーソル関連フラグ更新]
    H --> I
```

## データベース参照・更新仕様

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| ##BlitzCacheProcs | プランキャッシュ分析結果の格納 | グローバル一時テーブル |
| #statements | ステートメント情報の一時格納 | ローカル一時テーブル |

### テーブル別参照項目詳細

#### ##BlitzCacheProcs

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| SqlHandle | クエリプランの結合キー | WHERE SPID = @@SPID |
| is_optimistic_cursor | 楽観的カーソルフラグ | SET句で1に更新 |
| is_forward_only_cursor | 前方専用カーソルフラグ | SET句で1に更新 |

#### #statements

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| is_cursor | カーソルプラン判定 | EXISTS条件で使用 |
| SqlHandle | 結合キー | ##BlitzCacheProcsとの結合 |
| statement | XMLステートメントの解析 | nodes()関数でCursorPlan要素を抽出 |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| ##BlitzCacheProcs | UPDATE | is_optimistic_cursor、is_forward_only_cursorカラムを更新 |

#### ##BlitzCacheProcs更新

| 操作 | 項目（カラム名） | 更新値 | 備考 |
|-----|-----------------|-------|------|
| UPDATE | is_optimistic_cursor | 1 | CursorConcurrency="Optimistic"の場合 |
| UPDATE | is_forward_only_cursor | 1 | CursorActualType="FastForward"の場合 |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| XMLパースエラー | 不正なXMLフォーマット | エラーは発生しにくいが、存在チェックのみのため影響なし |
| メモリ不足 | 大量のプラン処理時 | @Topパラメータで対象を制限 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | なし |
| リトライ間隔 | - |
| リトライ対象エラー | - |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 制限なし |
| 1日あたり上限 | 制限なし |

### 配信時間帯

制限なし（sp_BlitzCache実行時に即座に出力）

## セキュリティ考慮事項

- 通知内容に機密情報は含まれない
- VIEW SERVER STATE権限が必要（sp_BlitzCache実行の前提条件）

## 備考

- この通知はsp_BlitzCacheの進捗表示の一部であり、カーソルプランが検出された場合のみ表示される
- WITH NOWAITオプションにより、バッファリングなしで即座にクライアントに送信される
- 重大度0は情報メッセージを意味し、エラーではない
- カーソルプランが存在しない場合は、代わりに「No cursor plans found, skipping」が出力される
- カーソル調査では楽観的カーソル（Optimistic）と前方専用カーソル（FastForward）を検出する

---

## コードリーディングガイド

本通知を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

まず、プロシージャで使用される一時テーブルの構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | sp_BlitzCache.sql | `sp_BlitzCache.sql` | ##BlitzCacheProcsテーブルのis_optimistic_cursor、is_forward_only_cursorカラム定義を確認 |
| 1-2 | sp_BlitzCache.sql | `sp_BlitzCache.sql` | #statementsテーブルのis_cursorカラムを確認 |

**読解のコツ**: is_cursorフラグは先行処理で設定され、カーソルプランの存在判定に使用される。

#### Step 2: エントリーポイントを理解する

処理の起点となるストアドプロシージャの構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | sp_BlitzCache.sql | `sp_BlitzCache.sql` | カーソル存在チェックと調査処理の分岐フロー |

**主要処理フロー**:
1. **行3545**: IF NOT EXISTSでカーソルプランの存在チェック
2. **行3548**: カーソルなしの場合「No cursor plans found, skipping」を出力
3. **行3552**: IF EXISTSでカーソルプランが存在する場合の処理開始
4. **行3555**: RAISERROR(N'Cursor plans found, investigating', 0, 1) WITH NOWAITでメッセージ出力

#### Step 3: カーソル詳細調査処理を理解する

カーソル調査の実際のロジックを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | sp_BlitzCache.sql | `sp_BlitzCache.sql` | 楽観的カーソルと前方専用カーソルの検出ロジック |

**主要処理フロー**:
- **行3557**: 「Checking for Optimistic cursors」メッセージ出力
- **行3558-3568**: CursorConcurrency="Optimistic"の検出
- **後続行**: CursorActualType="FastForward"の検出

### プログラム呼び出し階層図

```
sp_BlitzCache
    |
    +-- RAISERROR('Checking for expensive sorts')
    |       |
    |       +-- [sort_cost更新処理]
    |
    +-- IF NOT EXISTS(is_cursor = 1)
    |       |
    |       +-- RAISERROR('No cursor plans found, skipping')
    |
    +-- IF EXISTS(is_cursor = 1)
            |
            +-- RAISERROR('Cursor plans found, investigating')  <-- 本通知
                    |
                    +-- RAISERROR('Checking for Optimistic cursors')
                    |       |
                    |       +-- UPDATE ##BlitzCacheProcs SET is_optimistic_cursor = 1
                    |               |
                    |               +-- #statements との JOIN
                    |               |
                    |               +-- exist('//p:CursorPlan/@CursorConcurrency[.="Optimistic"]')
                    |
                    +-- [後続処理] Checking for FastForward cursors
```

### データフロー図

```
[入力]                      [処理]                                    [出力]

#statements.is_cursor   --> EXISTS判定                           --> 調査実行/スキップ
                               |
                               v (存在する場合)
#statements.statement   --> XMLネームスペース解析                --> ##BlitzCacheProcs.is_optimistic_cursor
(XMLクエリプラン)           nodes('/p:StmtCursor')                   ##BlitzCacheProcs.is_forward_only_cursor
                               |
                               v
                           exist('//p:CursorPlan/@CursorConcurrency')
                               |
                               v
                           コンソール出力 <-- RAISERROR
                           "Cursor plans found, investigating"
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| sp_BlitzCache.sql | `sp_BlitzCache.sql` | ソース | カーソルプラン調査処理を含むメインストアドプロシージャ |
