# 通知設計書 84-関数チェック

## 概要

本ドキュメントは、SQL Server First Responder Kitのsp_BlitzCache.sqlにおける「関数チェック」通知の設計を定義する。この通知は、クエリプランキャッシュの分析中にユーザー定義関数（UDF）およびCLR関数の使用を検出するチェック処理の開始を示す情報メッセージである。

### 本通知の処理概要

この通知は、sp_BlitzCacheストアドプロシージャがキャッシュされた実行プランを分析する際に、XMLクエリプラン内でユーザー定義関数（スカラー関数）およびCLR関数の使用を検出するチェック処理の開始を示す。

**業務上の目的・背景**：スカラーユーザー定義関数（UDF）はSQL Serverにおいて重大なパフォーマンス問題を引き起こす可能性がある。スカラー関数は行ごとに呼び出されるため、大量のデータを処理する際にパフォーマンスが著しく低下する。また、CLR関数も使用方法によってはパフォーマンスに影響を与える。これらの関数の使用を検出することで、インライン関数への置き換えや、関数ロジックのクエリへの直接組み込みなどの最適化を検討できる。

**通知の送信タイミング**：sp_BlitzCacheプロシージャの実行中、テーブル変数チェックが完了した後、かつ@ExpertModeが0より大きい場合に、関数チェック処理が開始される直前のタイミングで送信される。この通知はExpertModeが有効な場合にのみ表示される。

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

**通知内容の概要**：「Checking for functions」という英語メッセージが表示され、現在関数のチェック処理を実行中であることを示す。これにより、長時間実行される分析処理の進捗状況を把握できる。

**期待されるアクション**：通知の受信者は、処理が正常に進行していることを確認し、処理完了を待つ。分析結果においてスカラー関数が検出された場合は、その関数がクエリパフォーマンスに与える影響を評価し、インライン化または直接記述への変更を検討する。

## 通知種別

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

## 送信仕様

### 基本情報

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

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

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

## 通知テンプレート

### メール通知の場合

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

### 本文テンプレート

```
Checking for functions
```

### 添付ファイル

該当なし

## テンプレート変数

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

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| プロシージャ実行 | sp_BlitzCache内の関数チェック処理開始 | @ExpertMode > 0 | ExpertModeが有効な場合のみ通知 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| @ExpertMode = 0 | ExpertModeが無効の場合は通知されない |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[テーブル変数チェック完了] --> B{@ExpertMode > 0?}
    B -->|Yes| C[RAISERROR実行]
    B -->|No| I[スキップ]
    C --> D[メッセージ即時出力]
    D --> E[function_count/clr_function_count更新処理]
    E --> F[XMLクエリプラン解析]
    F --> G[UserDefinedFunction要素カウント]
    G --> H[##BlitzCacheProcs更新]
    H --> J[次のチェック処理へ]
    I --> J
```

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

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| ##BlitzCacheProcs | プランキャッシュ分析結果の格納 | グローバル一時テーブル |
| #relop | RelOp要素のXMLを一時格納 | ローカル一時テーブル |

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

#### ##BlitzCacheProcs

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| SqlHandle | クエリプランの結合キー | WHERE SPID = @@SPID |
| function_count | 非CLR関数の数 | SET句で更新 |
| clr_function_count | CLR関数の数 | SET句で更新 |

#### #relop

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| SqlHandle | 結合キー | ##BlitzCacheProcsとの結合 |
| relop | XMLプランの解析 | nodes()関数でScalarOperator要素を抽出 |

### 更新テーブル一覧

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

#### ##BlitzCacheProcs更新

| 操作 | 項目（カラム名） | 更新値 | 備考 |
|-----|-----------------|-------|------|
| UPDATE | function_count | 非CLR UDFの数 | count(distinct-values())で集計 |
| UPDATE | clr_function_count | CLR関数の数 | IsClrFunction="1"でフィルタ |

## エラー処理

### エラーケース一覧

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

### リトライ仕様

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

## 配信設定

### レート制限

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

### 配信時間帯

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

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

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

## 備考

- この通知はsp_BlitzCacheの進捗表示の一部であり、長時間実行時のユーザーへのフィードバックとして機能する
- WITH NOWAITオプションにより、バッファリングなしで即座にクライアントに送信される
- 重大度0は情報メッセージを意味し、エラーではない
- @ExpertMode > 0の場合にのみ実行される詳細チェックの一部
- 非CLR関数とCLR関数を別々にカウントし、それぞれ異なるカラムに格納する
- XPath式`//p:UserDefinedFunction[not(@IsClrFunction)]`で非CLR関数、`//p:UserDefinedFunction[@IsClrFunction = "1"]`でCLR関数を検出

---

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

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

### 推奨読解順序

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

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

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

**読解のコツ**: function_countとclr_function_countはINT型で、それぞれ非CLR UDFとCLR関数の使用数を格納する。

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

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | sp_BlitzCache.sql | `sp_BlitzCache.sql` | @ExpertModeによる条件分岐と関数チェックの処理フロー |

**主要処理フロー**:
1. **行3471**: IF @ExpertMode > 0 による条件分岐
2. **行3473**: RAISERROR(N'Checking for functions', 0, 1) WITH NOWAITでメッセージ出力
3. **行3474-3488**: XMLNAMESPACESとCTEを使用した関数チェックのUPDATE文実行

#### Step 3: XMLプラン解析処理を理解する

関数チェックの実際のロジックを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | sp_BlitzCache.sql | `sp_BlitzCache.sql` | CTEとXPath式によるUDF検出、count(distinct-values())による集計 |

**主要処理フロー**:
- **行3474**: XMLネームスペースの定義（showplanスキーマ）
- **行3477**: `count(distinct-values(//p:UserDefinedFunction[not(@IsClrFunction)]))` で非CLR関数をカウント
- **行3478**: `count(distinct-values(//p:UserDefinedFunction[@IsClrFunction = "1"]))` でCLR関数をカウント
- **行3480**: ComputeScalar/DefinedValues/DefinedValue/ScalarOperator要素を展開

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

```
sp_BlitzCache
    |
    +-- RAISERROR('Checking for table variables')
    |       |
    |       +-- [is_table_variable更新処理]
    |
    +-- IF @ExpertMode > 0
    |       |
    |       +-- RAISERROR('Checking for functions')  <-- 本通知
    |               |
    |               +-- WITH x AS (CTE定義)
    |               |       |
    |               |       +-- #relop との結合
    |               |       |
    |               |       +-- nodes('/p:RelOp/p:ComputeScalar/...')
    |               |       |
    |               |       +-- count(distinct-values()) による集計
    |               |
    |               +-- UPDATE ##BlitzCacheProcs
    |                       SET function_count, clr_function_count
    |
    +-- [後続処理] Checking for expensive key lookups
```

### データフロー図

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

#relop.relop            --> XMLネームスペース解析                --> ##BlitzCacheProcs.function_count
(XMLクエリプラン)           nodes('/p:RelOp/p:ComputeScalar/         ##BlitzCacheProcs.clr_function_count
                               p:DefinedValues/p:DefinedValue/
                               p:ScalarOperator')
                               |
                               v
                           count(distinct-values(
                               //p:UserDefinedFunction))
                               |
                               v
                           IsClrFunction属性で分類
                               |
                               v
                           コンソール出力 <-- RAISERROR
                           "Checking for functions"
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| sp_BlitzCache.sql | `sp_BlitzCache.sql` | ソース | 関数チェック処理を含むメインストアドプロシージャ |
