# 機能設計書 314-Find-DbaDbUnusedIndex

## 概要

本ドキュメントは、SQL Serverデータベース内の使用頻度が低いインデックスを検出するFind-DbaDbUnusedIndex機能の設計仕様を記載する。

### 本機能の処理概要

Find-DbaDbUnusedIndexは、sys.dm_db_index_usage_statsからインデックス使用統計を分析し、ほとんど使用されていないインデックスを特定する機能である。これらのインデックスは削除候補として、ストレージスペースの解放とデータ変更操作の高速化に貢献する。

**業務上の目的・背景**：インデックスはクエリパフォーマンスを向上させるために作成されるが、実際にはほとんど使用されないインデックスも多く存在する。未使用インデックスは以下の問題を引き起こす：ストレージスペースの浪費、INSERT/UPDATE/DELETE操作の遅延（インデックス更新オーバーヘッド）、インデックスメンテナンス（再構築・再編成）時間の増大、バックアップサイズの増大。本機能は、カスタマイズ可能な閾値を使用して「未使用」の定義を柔軟に設定でき、安全にインデックスを削除するための判断材料を提供する。

**機能の利用シーン**：本機能は以下のような場面で利用される。
- 定期的なインデックスメンテナンス時の削除候補特定
- ストレージコスト最適化プロジェクト
- パフォーマンスチューニング時のオーバーヘッド削減
- データベース統合・移行時の事前整理
- SQL Server再起動後の使用状況ベースライン確立

**主要な処理内容**：
1. SQL Serverインスタンスへの接続と最小稼働時間の検証
2. SQL Serverバージョンの既知の問題チェック（インデックス再構築によるDMVリセット問題）
3. 対象データベースの特定とアクセス可能性確認
4. カスタマイズ可能な閾値（Seeks、Scans、Lookups）に基づく未使用インデックス検索
5. サイズ、行数、圧縮情報を含む詳細な結果出力

**関連システム・外部連携**：SQL Server Management Objects (SMO)を使用してSQL Serverに接続し、sys.dm_db_index_usage_stats DMVを中心としたT-SQLクエリを実行する。

**権限による制御**：対象データベースへのVIEW DATABASE STATE権限が必要。SQL Server 2005（バージョン9）以降で動作する。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | インデックス管理 | 主機能 | 未使用インデックスの検出と一覧表示 |

## 機能種別

データ検索 / インデックス分析

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| SqlInstance | DbaInstanceParameter[] | No | 検索対象のSQL Serverインスタンス | SQL Server 2005以降 |
| SqlCredential | PSCredential | No | 代替認証資格情報 | Windows/SQL/AD認証サポート |
| Database | object[] | No | 検索対象データベース名（複数可） | - |
| ExcludeDatabase | object[] | No | 除外するデータベース名（複数可） | - |
| IgnoreUptime | switch | No | 7日間の稼働時間チェックをバイパス | - |
| Seeks | int | No | user_seeks閾値（デフォルト: 1） | 1-1000000 |
| Scans | int | No | user_scans閾値（デフォルト: 1） | 1-1000000 |
| Lookups | int | No | user_lookups閾値（デフォルト: 1） | 1-1000000 |
| InputObject | Microsoft.SqlServer.Management.Smo.Database[] | No | Get-DbaDatabaseからのパイプライン入力 | - |
| EnableException | switch | No | 例外処理の有効化 | - |

### 入力データソース

- SQL Serverインスタンスへの直接接続
- Get-DbaDatabaseからのパイプライン入力

## 出力仕様

### 出力データ

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

### 出力先

- PowerShellパイプライン（PSCustomObject形式）

## 処理フロー

### 処理シーケンス

```
1. SQLクエリ定義（beginブロック）
   └─ 圧縮情報用のプレースホルダを含むクエリを定義
2. InputObject処理
   └─ SqlInstance指定時はGet-DbaDatabaseを呼び出し
3. データベースループ
   └─ 各データベースに対して処理を実行
4. バージョンチェック
   └─ SQL Server 2005未満はスキップ
5. 稼働時間チェック
   └─ tempdb作成日から7日以上経過を確認
6. 既知の問題チェック
   └─ SQL 2012 SP3 CU3未満、SQL 2014 SP2未満は警告
7. クエリ実行
   └─ バージョンに応じて圧縮情報を追加
8. 結果出力
   └─ 閾値未満のインデックスを出力
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[SQLクエリ定義]
    B --> C{SqlInstance指定?}
    C -->|Yes| D[Get-DbaDatabase呼び出し]
    C -->|No| E[InputObject使用]
    D --> F[foreach database]
    E --> F
    F --> G{Version < 9?}
    G -->|Yes| H[スキップ]
    G -->|No| I[tempdb作成日取得]
    I --> J{稼働7日以上?}
    J -->|No| K{IgnoreUptime?}
    K -->|No| L[エラー: 稼働時間不足]
    K -->|Yes| M[警告出力, 続行]
    J -->|Yes| N[既知問題チェック]
    M --> N
    N --> O{SQL 2012 CU未達?}
    O -->|Yes| P[エラー: 既知問題]
    O -->|No| Q{Version > 9?}
    Q -->|Yes| R[圧縮情報追加]
    Q -->|No| S[そのままのクエリ]
    R --> T[db.Query実行]
    S --> T
    T --> U[結果出力]
    U --> F
    L --> V[次のDB]
    P --> V
    H --> V
    V --> W[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | 最小バージョン要件 | SQL Server 2005（バージョン9）以降が必要 | 接続時 |
| BR-002 | 稼働時間要件 | 信頼性のある統計には7日以上の稼働が推奨 | 常時 |
| BR-003 | 既知問題チェック | SQL 2012 SP3 CU3未満、SQL 2014 SP2未満はDMVバグあり | 該当バージョン |
| BR-004 | 未使用判定 | user_seeks < Seeks AND user_scans < Scans AND user_lookups < Lookups | 常時 |
| BR-005 | HEAP/Columnstore除外 | type_desc NOT IN ('HEAP', 'CLUSTERED COLUMNSTORE') | 常時 |
| BR-006 | MSShipped除外 | OBJECTPROPERTY(IsMSShipped) = 0 | 常時 |

### 計算ロジック

```sql
-- インデックスサイズの計算
IndexSizeMB = SUM(used_page_count) * 8 / 1024.0

-- 稼働日数の計算
diffDays = (Get-Date) - tempdb.CreateDate
```

## データベース操作仕様

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 未使用インデックス検索 | sys.tables, sys.schemas, sys.indexes, sys.databases, sys.dm_db_index_usage_stats, sys.dm_db_partition_stats, sys.partitions | SELECT | DMVとカタログビューから未使用インデックス情報を取得 |

### テーブル別操作詳細

#### sys.dm_db_index_usage_stats

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | user_seeks, user_scans, user_lookups, user_updates, last_user_* | 閾値未満のインデックス | LEFT OUTER JOINで結合 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | UptimeError | 稼働7日未満でIgnoreUptime未指定 | IgnoreUptimeを指定するか待機 |
| - | KnownIssue | SQL 2012/2014の既知バグ該当バージョン | パッチ適用を推奨 |
| - | InvalidOperation | クエリ実行エラー | データベースアクセス権限を確認 |
| - | VersionError | SQL Server 2005未満 | アップグレードが必要 |

### リトライ仕様

リトライ機能は実装されていない。

## トランザクション仕様

本機能は読み取り専用であり、トランザクション管理は不要。

## パフォーマンス要件

- CTEを使用した効率的なクエリ構造
- DMVの使用による高速なデータ取得

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

- SqlCredentialによる認証情報の安全な受け渡し
- VIEW DATABASE STATE権限が必要

## 備考

- SQL Server再起動でDMV統計はリセットされる
- インデックス再構築でも統計がリセットされる（SQL 2012/2014の一部バージョン）
- 33日以上の稼働で警告メッセージが表示される

---

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

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

### 推奨読解順序

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

出力されるオブジェクトの構造を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Find-DbaDbUnusedIndex.ps1 | `public/Find-DbaDbUnusedIndex.ps1` | .OUTPUTSコメントで出力プロパティを確認 |

**読解のコツ**: user_*とsystem_*の両方の統計が含まれることに注意。

#### Step 2: SQLクエリを理解する

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

**主要処理フロー**:
- **146-170行目**: CTE_IndexSpaceでサイズ計算
- **171-218行目**: メインSELECTで統計情報取得
- **216-218行目**: 閾値条件（パラメータ埋め込み）

#### Step 3: 稼働時間と既知問題チェックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | Find-DbaDbUnusedIndex.ps1 | `public/Find-DbaDbUnusedIndex.ps1` | 252-277行目の検証ロジック |

**主要処理フロー**:
- **252-254行目**: tempdb.CreateDateから稼働日数計算
- **256-262行目**: 7日未満チェックとIgnoreUptime処理
- **271-273行目**: SQL 2012/2014の既知バグチェック

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | Find-DbaDbUnusedIndex.ps1 | `public/Find-DbaDbUnusedIndex.ps1` | 284-286行目のバージョン別クエリ調整 |

**主要処理フロー**:
- **284-286行目**: Version > 9 で圧縮情報プレースホルダを置換

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

```
Find-DbaDbUnusedIndex
    │
    ├─ Get-DbaDatabase (SqlInstance指定時)
    │      └─ Connect-DbaInstance
    │
    └─ Database.Query($sqlToRun)
           └─ T-SQL クエリ実行
```

### データフロー図

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

SqlInstance ───────────▶ Get-DbaDatabase ───────▶ Database オブジェクト
        │                                              │
        ▼                                              ▼
InputObject ──────────────────────────────────▶ 検証処理
        │                                              │
        ▼                                              ▼
Seeks/Scans/Lookups ───▶ SQLクエリ閾値埋め込み ───▶ 実行クエリ
        │                                              │
        ▼                                              ▼
                                              Database.Query()
                                                       │
                                                       ▼
                                              PSCustomObject ───▶ パイプライン出力
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Find-DbaDbUnusedIndex.ps1 | `public/Find-DbaDbUnusedIndex.ps1` | ソース | メイン機能実装 |
| Find-DbaDbDisabledIndex.ps1 | `public/Find-DbaDbDisabledIndex.ps1` | ソース | 関連機能（無効化インデックス検出） |
| Find-DbaDbDuplicateIndex.ps1 | `public/Find-DbaDbDuplicateIndex.ps1` | ソース | 関連機能（重複インデックス検出） |
| Get-DbaDatabase.ps1 | `public/Get-DbaDatabase.ps1` | ソース | データベース取得（依存先） |
