SQL Server インデックス再入門

# インデックス再入門

SQL Server インデックス再入門の回へようこそ！
みなさん、こんにちは！
本日は「SQL Server インデックス再入門」にご参加いただきありがとうございます。この会は、SQL Serverを扱うすべての方、特にインデックスについて「改めて学び直したい」「基礎から深く理解したい」という初心者から中級者の方々を対象にした学びの場です。

インデックスとは？ なぜ重要なのか？
データベースにおけるインデックスは、私たちが日々の業務でデータを検索したり分析したりするときのスピードと効率を大きく左右する、まさに「データベースのアクセル」のような存在です。しかし、インデックスは適切に設計・活用されなければ逆にパフォーマンスを低下させる要因にもなり得ます。

今日の目標
本日のセッションでは、以下のポイントを中心に学んでいきます：

インデックスの基本構造と役割
パフォーマンス向上に役立つ設計のコツ
インデックスがクエリに与える具体的な影響
実際にデータベースを使ってインデックスを試してみる実践的なデモ
こんな方におすすめです
データベース初心者で、インデックスの基本から学びたい
SQL Serverを日々使用しているが、インデックスについて疑問を持っている
クエリパフォーマンスの改善に悩んでいる
一緒に楽しみながら学びましょう！
学びは理論だけでなく、実践があってこそ身につくものです。本日のセッションでは、簡単なデモや例題を通して、インデックスがどのように動作するかを体感いただけるように準備しています。ぜひ、気軽に質問やディスカッションを楽しみながら、一緒に学びを深めていきましょう。

最後に、インデックスの世界は奥深く、今日の内容がその入り口となれば幸いです。それでは、さっそく始めましょう！

インデックスは、データベース内のデータを効率よく検索するための仕組みです。本で言えば索引、電話帳で言えば名前順や住所順のように整理されています。例えば、インデックスがないとデータベース全体を隅々まで探さないといけませんが、インデックスがあると該当部分をすぐに特定できるので、とても速くなります。

## インデックスのそもそもの存在理由

1. 検索を速くするため
インデックスは、テーブル内のデータを「本の索引」のように整理する役割を持っています。例えば、数千ページある本の中から特定のトピックを探す場合、索引があればすぐに見つけられます。同様に、データベース内で特定の条件に合うデータを素早く探すためにインデックスが存在します。

例:
「特定の顧客の注文情報を探す」
「特定の日付範囲内の売上データを集計する」

2. クエリのパフォーマンスを向上
データベースは大量のデータを格納しています。全てのデータを逐一検索する（フルスキャン）と時間がかかります。インデックスを利用することで、必要なデータだけを効率よく取り出せるため、クエリの実行時間が大幅に短縮されます。

3. データの整理と構造化
インデックスはデータの構造を整えることで、特定の列（カラム）に基づいて順序を付けたり、クエリを効率化するための情報を提供します。これにより、並び替えやグループ化なども速やかに行えます。

SQL Serverにおけるインデックスには、目的や構造に応じてさまざまな種類があります。それぞれの特徴と役割を理解することで、適切なインデックス設計が可能になります。

---

### **SQL Serverの主なインデックスの種類**

#### 1. **クラスタードインデックス (Clustered Index)**
- **特徴**:
  - テーブルのデータそのものをソートして格納するインデックス。
  - テーブルに1つしか作成できない。
- **例**:
  - 書籍の内容をページ順に並べ替えるイメージ。
- **用途**:
  - 主キー（PRIMARY KEY）は通常クラスタードインデックスで作成される。
  - 順序付き検索や範囲クエリに最適。

---

#### 2. **非クラスタードインデックス (Nonclustered Index)**
- **特徴**:
  - テーブルデータとは別に作成されるインデックス。
  - インデックスに格納されたデータは「元データのポインタ」として機能。
  - 1つのテーブルに複数作成可能。
- **例**:
  - 書籍の索引（目次やテーマ別キーワードのリスト）。
- **用途**:
  - 特定の列で検索を高速化したい場合に有効。
  - 頻繁に参照される列に適している。

---

#### 3. **ユニークインデックス (Unique Index)**
- **特徴**:
  - 重複を許さないインデックス。
  - 各行が一意であることを保証。
- **用途**:
  - メールアドレスやユーザー名など、重複を避けたいデータ列に利用。
  - PRIMARY KEYやUNIQUE制約を伴う列に自動的に作成される。

---

#### 4. **カラムストアインデックス (Columnstore Index)**
- **特徴**:
  - データを列単位で格納するインデックス。
  - 通常の行単位ストレージとは異なる形式で保存。
  - 圧縮効率が高く、大規模なデータ分析に最適。
- **用途**:
  - データウェアハウスや分析クエリでのパフォーマンス向上に使用。

---

#### 5. **フィルタードインデックス (Filtered Index)**
- **特徴**:
  - 特定の条件を満たす行だけを対象としたインデックス。
  - フルテーブルではなく部分的なデータに適用。
- **用途**:
  - NULL値を除外したり、特定の条件を満たすデータのみを索引化したい場合。

---

#### 6. **XMLインデックス**
- **特徴**:
  - XMLデータ型の列に対して作成されるインデックス。
  - XMLデータを高速に検索・クエリするために設計。
- **用途**:
  - XMLデータを頻繁にクエリするアプリケーション。

---

#### 7. **全文検索インデックス (Full-text Index)**
- **特徴**:
  - テキスト列の中で特定の単語やフレーズを効率的に検索可能。
  - Microsoft Full-Text Engineを利用。
- **用途**:
  - 文章や説明文の中でのキーワード検索。

---

#### 8. **ハッシュインデックス (Hash Index)**
- **特徴**:
  - メモリ最適化テーブル（In-Memory OLTP）のためのインデックス。
  - ハッシュテーブルを使用してデータを高速に検索。
- **用途**:
  - メモリ最適化されたワークロードで高速検索が必要な場合。

---

### **補足情報**
- インデックスを正しく設計・使用することで、クエリパフォーマンスを大幅に向上できます。
- ただし、過剰なインデックス作成や不適切な設計は、更新・挿入操作のパフォーマンスを低下させるリスクがあります。



使用可能なインデックスの種類

https://learn.microsoft.com/ja-jp/sql/relational-databases/indexes/indexes?view=sql-server-ver16#available-index-types


## インデックスは何故早い

### **インデックスは何故早いのか？**

データベースのインデックスは、本の索引や電話帳のような役割を果たします。それにより、大量のデータの中から必要な情報を素早く見つけることができ、クエリの実行を効率化します。では、インデックスが「速さ」をもたらす理由を見ていきましょう。

---

#### **1. インデックスはデータをソートしている**

インデックスは、特定の列に基づいてデータを事前にソートした状態で保持します。このソートが、検索処理を飛躍的に効率化します。

- **ポイント**:
  - ソートされていることで、検索処理が「二分探索法」などの効率的なアルゴリズムを利用可能。
  - ソート済みデータを使うことで、範囲検索（例: `BETWEEN` や `>` 条件）が高速化。

- **具体例**:
  - 名前の列にインデックスがある場合、「田中さん」を探すときは、全てのデータを最初から最後まで調べる必要がなく、「タ行」に絞って検索できます。

---

#### **2. リソースへのアクセスが減る**

インデックスを使うことで、データベースがクエリを処理する際のリソース消費を大幅に削減できます。

- **ポイント**:
  - 通常、テーブル全体を読み取るフルスキャン（全件走査）は、すべてのデータページにアクセスする必要があります。
  - インデックスは、データの一部だけを参照するため、必要なページへのアクセスを最小限に抑えます。

- **仕組み**:
  - 非クラスタードインデックスの場合、データの実体には直接アクセスせず、インデックスページを参照して効率的に必要なデータを取得。
  - クラスタードインデックスの場合、データそのものがソートされているため、アクセスする範囲を特定しやすい。

- **具体例**:
  - 100万件のデータから特定の注文IDを検索する場合、フルスキャンでは全100万行を調査。
  - インデックスがあれば、関連する数十行だけにアクセスして結果を取得。


### **まとめ**

- **速さの理由**:
  1. **データがソートされていること**による効率的な検索。
  2. **必要なリソースへのアクセスが減ること**による負荷の軽減。

インデックスは正しく活用すれば、検索クエリのパフォーマンスを大幅に向上させる強力なツールです。ただし、インデックスが適切に設計されていないと、データの更新や挿入時に逆に負荷が増える場合もあるため、状況に応じた使い方が重要です。


# Apendex

tableのcreate

```
CREATE TABLE [dbo].[ParentTable](
	[ColA] [int] NOT NULL,
	[ParentData] [nvarchar](100) NULL,
	[SubA] [int] NULL,
PRIMARY KEY CLUSTERED 
(
	[ColA] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]

CREATE TABLE [dbo].[DemoTable](
	[ID] [int] NOT NULL,
	[ColA] [int] NULL,
	[ColB] [int] NULL,
	[ColC] [nvarchar](100) NULL,
PRIMARY KEY CLUSTERED 
(
	[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]

CREATE TABLE [dbo].[ChildTable](
	[ChildID] [int] IDENTITY(1,1) NOT NULL,
	[ColA] [int] NOT NULL,
	[ColB] [int] NOT NULL,
	[ChildData] [nvarchar](100) NULL,
	[SubA] [int] NULL,
	[SubB] [int] NULL,
PRIMARY KEY CLUSTERED 
(
	[ChildID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
```

データー投入
```
-- ParentTable に100,000件のデータを挿入
DECLARE @i INT = 1;
WHILE @i <= 100000
BEGIN
    INSERT INTO ParentTable (ColA, ParentData, SubA)
    VALUES (@i, CONCAT('Parent Data ', @i), @i);
    SET @i = @i + 1;
END;

-- DemoTable に1,000,000件のデータを挿入
SET @i = 1;
WHILE @i <= 1000000
BEGIN
    INSERT INTO DemoTable (ID, ColA, ColB, ColC)
    VALUES (@i, FLOOR((@i - 1) / 10) + 1, @i % 10, CONCAT('Demo Data ', @i));
    SET @i = @i + 1;
END;

-- ChildTable に10,000,000件のデータを挿入
SET @i = 1;
WHILE @i <= 10000000
BEGIN
    INSERT INTO ChildTable (ColA, ColB, ChildData, SubA, SubB)
    VALUES (FLOOR((@i - 1) / 100) + 1, @i % 10, CONCAT('Child Data ', @i),FLOOR((@i - 1) / 100) + 1, @i % 10);
    SET @i = @i + 1;
END;
```

tableをtruncate
```
TRUNCATE TABLE ChildTable;
TRUNCATE TABLE DemoTable;
TRUNCATE TABLE ParentTable;
```

禁断のフラグ
```
-- 統計情報ヘッダ
DBCC SHOW_STATISTICS (tab1,PK_Tab1) WITH STAT_HEADER
GO
-- 統計情報ヒストグラム
DBCC SHOW_STATISTICS (tab1,PK_Tab1) WITH HISTOGRAM
GO
-- 密度ベクトル
DBCC SHOW_STATISTICS (tab1,PK_Tab1) WITH DENSITY_VECTOR
GO


DBCC TRACEON (8605,8606,8607,8612,8615,8760,3604,-1)

```