# 画面設計書 265-未使用インデックス検索

## 概要

本ドキュメントは、dbatoolsの未使用インデックス検索機能に関する画面設計書です。使用頻度が低く、削除候補となるインデックスを識別し、データベースパフォーマンスの最適化を支援するためのPowerShellコマンドを定義します。

### 本画面の処理概要

本機能は、sys.dm_db_index_usage_statsのインデックス使用統計を分析し、活動が最小限で、ストレージスペースを消費しデータ変更を遅くしている一方でクエリパフォーマンス向上に貢献していないインデックスを識別するFind-DbaDbUnusedIndexコマンドを提供します。

**業務上の目的・背景**：DBAs がデータベースパフォーマンスを最適化する際、ほとんど使用されていないインデックスを見つけることで、メンテナンスオーバーヘッドの削減、INSERT/UPDATE/DELETE 操作の高速化、ディスクスペースの解放が可能になります。本機能は、カスタマイズ可能な seek/scan/lookup のしきい値を使用して「未使用」を定義し、信頼性のある統計を得るためにSQL Server の稼働時間（7日以上）を確認する安全チェックを含みます。

**画面へのアクセス方法**：PowerShellコンソールから以下のコマンドを実行してアクセスします。
```powershell
Find-DbaDbUnusedIndex -SqlInstance <インスタンス名> -Database <データベース名>
```

**主要な操作・処理内容**：
1. 指定されたSQL Serverインスタンスへの接続
2. SQL Server稼働時間の検証（7日以上推奨）
3. sys.dm_db_index_usage_statsを使用した使用統計の取得
4. Seeks/Scans/Lookupsのしきい値による未使用判定
5. インデックスサイズ、行数、圧縮設定等の詳細情報取得
6. 使用パターンの詳細（最終使用日時、システム操作含む）の出力

**画面遷移**：
- `Connect-DbaInstance`で接続を確立後に実行可能
- `Get-DbaDatabase`からパイプライン入力可能
- 取得結果は`Export-Csv`等で出力可能

**権限による表示制御**：
- データベースへの接続権限が必要
- sys.dm_db_index_usage_stats、sys.dm_db_partition_stats等のDMVへのSELECT権限が必要

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 314 | Find-DbaDbUnusedIndex | 主機能 | 未使用インデックスの検索 |

## 画面種別

CLI（コマンドラインインターフェース）- PowerShellコマンド

## URL/ルーティング

PowerShellコマンド: `Find-DbaDbUnusedIndex`

## 入出力項目

### パラメータ

| パラメータ名 | 型 | 必須 | 説明 |
|-------------|-----|------|------|
| SqlInstance | DbaInstanceParameter[] | No | 対象SQL Serverインスタンス（パイプライン入力対応） |
| SqlCredential | PSCredential | No | 代替認証情報 |
| Database | object[] | No | 対象データベース（ワイルドカード対応） |
| ExcludeDatabase | object[] | No | 除外するデータベース |
| IgnoreUptime | switch | No | 稼働時間チェックをバイパス |
| Seeks | int | No | Seeksのしきい値（デフォルト: 1、範囲: 1-1000000） |
| Scans | int | No | Scansのしきい値（デフォルト: 1、範囲: 1-1000000） |
| Lookups | int | No | Lookupsのしきい値（デフォルト: 1、範囲: 1-1000000） |
| InputObject | Database[] | No | パイプライン入力（Get-DbaDatabase出力） |
| EnableException | switch | No | 例外を有効化 |

## 表示項目

### 出力オブジェクト（PSCustomObject）

| 項目名 | 型 | 説明 |
|--------|-----|------|
| ComputerName | string | SQL Serverのコンピュータ名 |
| InstanceName | string | SQL Serverインスタンス名 |
| SqlInstance | string | 完全なインスタンス名 |
| Database | string | データベース名 |
| DatabaseId | int | データベースID |
| Schema | string | スキーマ名 |
| Table | string | テーブル名 |
| ObjectId | int | テーブルオブジェクトID |
| IndexName | string | インデックス名 |
| IndexId | int | インデックスID |
| TypeDesc | string | インデックスタイプ |
| UserSeeks | int | ユーザークエリによるSeek操作回数 |
| UserScans | int | ユーザークエリによるScan操作回数 |
| UserLookups | int | ユーザークエリによるLookup操作回数 |
| UserUpdates | int | ユーザークエリによる更新操作回数 |
| LastUserSeek | datetime | 最終ユーザーSeek日時 |
| LastUserScan | datetime | 最終ユーザーScan日時 |
| LastUserLookup | datetime | 最終ユーザーLookup日時 |
| LastUserUpdate | datetime | 最終ユーザー更新日時 |
| SystemSeeks | int | システムクエリによるSeek操作回数 |
| SystemScans | int | システムクエリによるScan操作回数 |
| SystemLookup | int | システムクエリによるLookup操作回数 |
| SystemUpdates | int | システムクエリによる更新操作回数 |
| LastSystemSeek | datetime | 最終システムSeek日時 |
| LastSystemScan | datetime | 最終システムScan日時 |
| LastSystemLookup | datetime | 最終システムLookup日時 |
| LastSystemUpdate | datetime | 最終システム更新日時 |
| IndexSizeMB | decimal | インデックスサイズ（MB） |
| RowCount | int | インデックス行数 |
| CompressionDescription | string | データ圧縮タイプ（SQL Server 2008以降） |

## イベント仕様

### 1-Find-DbaDbUnusedIndex実行

1. beginブロックでSQLクエリを定義（バージョン別置換用のプレースホルダー付き）
2. InputObjectまたはSqlInstanceからデータベース取得
3. 各データベースに対して以下を実行:
   - 稼働時間チェック（7日未満の場合は警告またはスキップ）
   - SQL Server 2012/2014の既知問題チェック
   - バージョンに応じたクエリ調整（圧縮情報の追加）
   - クエリ実行と結果出力

### 2-稼働時間チェック

- tempdbの作成日時からSQL Serverの最終再起動日を取得
- 7日以上の稼働を推奨（統計の信頼性確保）
- 33日未満の場合は警告メッセージを出力
- IgnoreUptime指定時はチェックをスキップ

### 3-既知問題チェック

- SQL Server 2012 SP3 CU3未満（Build 6537未満）は問題あり
- SQL Server 2014 SP2未満（Build 5000未満）は問題あり
- インデックス再構築時にsys.dm_db_index_usage_statsの行がクリアされる既知問題

## データベース更新仕様

### 操作別データベース影響一覧

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| Find-DbaDbUnusedIndex | sys.tables | SELECT | テーブル情報の取得 |
| Find-DbaDbUnusedIndex | sys.schemas | SELECT | スキーマ情報の取得 |
| Find-DbaDbUnusedIndex | sys.indexes | SELECT | インデックス定義の取得 |
| Find-DbaDbUnusedIndex | sys.databases | SELECT | データベース情報の取得 |
| Find-DbaDbUnusedIndex | sys.dm_db_index_usage_stats | SELECT | 使用統計の取得 |
| Find-DbaDbUnusedIndex | sys.dm_db_partition_stats | SELECT | パーティション統計の取得 |
| Find-DbaDbUnusedIndex | sys.partitions | SELECT | パーティション情報の取得 |

本コマンドは読み取り専用操作のみを行い、データベースの変更は行いません。

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 発生条件 |
|-------------|------|--------------|---------|
| MSG001 | 警告 | Database [{Database}] was not found on [{SqlInstance}]. | 指定データベースが見つからない |
| MSG002 | 警告 | Database [{db}] is not accessible. | データベースにアクセス不可 |
| MSG003 | エラー | This function does not support versions lower than SQL Server 2005 (v9). | SQL 2005未満 |
| MSG004 | エラー | The SQL Service on {instance} was restarted on {lastRestart}, which is not long enough for a solid evaluation. | 稼働時間7日未満（IgnoreUptime未指定） |
| MSG005 | 情報 | The SQL Service was restarted on {lastRestart}, which is not long enough for a solid evaluation. | 稼働時間7日未満（IgnoreUptime指定） |
| MSG006 | 情報 | The SQL Service on {instance} was restarted on {lastRestart}, which may not be long enough for a solid evaluation. | 稼働時間33日未満 |
| MSG007 | エラー | This SQL version has a known issue... | SQL 2012 SP3 CU3未満またはSQL 2014 SP2未満 |
| MSG008 | エラー | Issue gathering indexes | クエリ実行エラー |

## 例外処理

| 例外条件 | 処理内容 |
|---------|---------|
| データベースが見つからない | 警告メッセージを出力して続行 |
| データベースアクセス不可 | 警告メッセージを出力して次のデータベースへ |
| SQL Server 2005未満 | Stop-Functionでエラーを報告し次へ |
| 稼働時間不足 | IgnoreUptime未指定時はStop-Function、指定時はVerboseメッセージ |
| 既知問題のあるバージョン | Stop-Functionでエラーを報告し次へ |
| クエリ実行エラー | Stop-FunctionでInvalidOperationカテゴリのエラーを報告 |

## 備考

- SQL Server 2005以降に対応
- SQL Server 2008以降ではデータ圧縮情報（CompressionDescription）を含む
- デフォルトの未使用判定: user_seeks < 1 AND user_scans < 1 AND user_lookups < 1
- HEAPとCLUSTERED COLUMNSTOREは除外（type_desc NOT IN）
- MSShippedオブジェクトは除外（OBJECTPROPERTY = 0）
- Seeks/Scans/Lookupsパラメータで判定しきい値をカスタマイズ可能

---

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

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

### 推奨読解順序

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

本コマンドで使用されるSQLクエリの構造を理解することが重要です。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Find-DbaDbUnusedIndex.ps1 | `public/Find-DbaDbUnusedIndex.ps1` | 行145-219の$sqlクエリ |

**読解のコツ**: CTE_IndexSpaceとメインクエリの構造、WHERE句の未使用判定条件（user_seeks < $Seeks等）を理解します。

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

beginブロックとprocessブロックの処理フローを理解します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | Find-DbaDbUnusedIndex.ps1 | `public/Find-DbaDbUnusedIndex.ps1` | 行143-226のbeginブロック |
| 2-2 | Find-DbaDbUnusedIndex.ps1 | `public/Find-DbaDbUnusedIndex.ps1` | 行228-294のprocessブロック |

**主要処理フロー**:
1. **145-219行**: $sql変数にT-SQLクエリを定義（パラメータ埋め込み）
2. **221-225行**: 置換用のプレースホルダー定義
3. **229-231行**: InputObjectへのデータベース追加
4. **252-262行**: 稼働時間チェック（7日以上推奨）
5. **271-273行**: 既知問題のあるバージョンチェック
6. **284-286行**: SQL 2008以降では圧縮情報を追加
7. **289行**: db.Query($sqlToRun)でクエリ実行

#### Step 3: バージョン別処理を理解する

SQL Serverバージョンによる処理分岐を理解します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | Find-DbaDbUnusedIndex.ps1 | `public/Find-DbaDbUnusedIndex.ps1` | 行248-249, 271-273, 284-286 |

**主要処理フロー**:
- **248-249行**: VersionMajor < 9でSQL 2005未満をチェック
- **271-273行**: SQL 2012/2014の特定ビルド番号チェック
- **284-286行**: VersionMajor > 9で圧縮情報のプレースホルダー置換

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

```
Find-DbaDbUnusedIndex
    |
    +-- begin block
    |       +-- $sql 変数にT-SQLクエリ定義
    |       +-- 置換用プレースホルダー定義
    |
    +-- process block
            |
            +-- Get-DbaDatabase (SqlInstance指定時)
            |
            +-- foreach ($db in $InputObject)
                    |
                    +-- $server = $db.Parent
                    |
                    +-- バージョンチェック (VersionMajor < 9)
                    |
                    +-- 稼働時間チェック (tempdb.CreateDate)
                    |       +-- diffDays <= 6: エラーまたは警告
                    |       +-- diffDays <= 33: 警告
                    |
                    +-- 既知問題チェック
                    |       +-- SQL 2012 Build < 6537
                    |       +-- SQL 2014 Build < 5000
                    |
                    +-- SQL 2008以降: 圧縮情報追加
                    |       +-- Replace($replaceParamCTE, $replaceValueCTE)
                    |       +-- Replace($replaceParamSelect, $replaceValueSelect)
                    |
                    +-- $db.Query($sqlToRun)
                            |
                            +-- CTE_IndexSpace
                            |       +-- sys.dm_db_partition_stats
                            |       +-- sys.partitions
                            |
                            +-- メインクエリ
                                    +-- sys.tables
                                    +-- sys.schemas
                                    +-- sys.indexes
                                    +-- sys.databases
                                    +-- sys.dm_db_index_usage_stats
```

### データフロー図

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

SqlInstance/         +------------------+
InputObject      -->| Get-DbaDatabase  |
Database/            | (必要時)         |
ExcludeDatabase      +--------+---------+
                              |
                              v
                     +------------------+
IgnoreUptime     -->| 稼働時間チェック  |
                     | (7日以上推奨)    |
                     +--------+---------+
                              |
                              v
                     +------------------+
                     | バージョン       |
                     | チェック         |
                     +--------+---------+
                              |
                              v
Seeks/Scans/         +------------------+
Lookups          -->| クエリ構築       |
                     | ($sqlToRun)      |
                     +--------+---------+
                              |
                              v
                     +------------------+
                     | db.Query()       |---> PSCustomObject[]
                     +------------------+     (未使用インデックス情報)
                              |
                              v
                     WHERE user_seeks < $Seeks
                       AND user_scans < $Scans
                       AND user_lookups < $Lookups
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Find-DbaDbUnusedIndex.ps1 | `public/Find-DbaDbUnusedIndex.ps1` | ソース | 未使用インデックス検索の主要ロジック |
| Get-DbaDatabase.ps1 | `public/Get-DbaDatabase.ps1` | 依存 | データベースオブジェクト取得 |
