SQL Server/SQL Database再入門 第4回 SQL Databaseでパーティション テーブルとパーティション インデックス

# パーティションテーブルを作る

SQL Serverの場合は物理的な配置先となるファイルグループをパーティションごとに割り当てることができますが、SQL Database では、PRIMARYファイル グループのみがサポートされています。

PARTITION FUNCTIONは値をパーティションに分割するための関数です。以下の場合は固定値でマッピングしています。

```sql
CREATE PARTITION FUNCTION partitionFunction (int)  
    AS RANGE LEFT FOR VALUES (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) ;  
GO
```

PARTITION SCHEMEはPARTITION FUNCTIONによって分割されたPARTITIONとファイル グループをマッピングするためのものなのですが、SQL Databaseの場合はPRIMARYしか使えないのでほぼ定型文になります。

```sql
CREATE PARTITION SCHEME partitionScheme1
    AS PARTITION partitionFunction  
     ALL TO ('PRIMARY');  
GO
```

以下のテーブルを試しに分割してみます。

```sql
CREATE TABLE PartitionTable (
	col1 INT IDENTITY(1, 1),
	col2 AS col1 % 10 PERSISTED,
	col3 NVARCHAR(10),
	CONSTRAINT PK_PartitionTable PRIMARY KEY CLUSTERED (col1, col2)
)  ON partitionScheme1 (col2) ;  
GO
```

検証用のデータを流し込んでみます。

```sql
INSERT INTO PartitionTable
(col3) VALUES ('AAA')
GO 10000
INSERT INTO PartitionTable
(col3) VALUES ('BBB')
GO 10000
INSERT INTO PartitionTable
(col3) VALUES ('CCC')
GO 10000
INSERT INTO PartitionTable
(col3) VALUES ('DDD')
GO 10000
```

パーティション別の状態を確認してみましょう。

```sql
SELECT 
t.name AS [table], 
i.name AS [index], 
p.partition_number, 
r.value AS boundary,
p.rows
FROM sys.tables AS t  
JOIN sys.indexes AS i  
    ON t.object_id = i.object_id  
JOIN sys.partitions AS p  
    ON i.object_id = p.object_id AND i.index_id = p.index_id   
JOIN  sys.partition_schemes AS s   
    ON i.data_space_id = s.data_space_id  
JOIN sys.partition_functions AS f   
    ON s.function_id = f.function_id  
LEFT JOIN sys.partition_range_values AS r   
    ON f.function_id = r.function_id and r.boundary_id = p.partition_number  
WHERE t.name = 'PartitionTable' AND i.type <= 1  
ORDER BY p.partition_number;
```

11個のパーティションができていて、1から10までには4000件ずつ入っていますね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16609/04031a5d-425c-5b99-202f-253ec0a43537.png)

# パーティションテーブルにクエリを投げる

以下のクエリはパーティション分割されていなければ論理読み取りの回数はインデックスの深さと一致するはずですが、今回の結果は違います。

```sql
SET STATISTICS IO, TIME ON

SELECT
*
FROM PartitionTable
WHERE col1 = 100

SET STATISTICS IO, TIME OFF
```

論理読み取り数は20になっていますね。これは11あるパーティションのうち一つの中身は空で、それ以外の2階層あるインデックスをSEEKしたので、`10 * 2 = 20`になったのです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16609/cf1a52a1-c275-3f10-26b1-85b210bd2f8a.png)

パーティションの分割列を指定してみましょう。

```sql
SET STATISTICS IO, TIME ON

SELECT
*
FROM PartitionTable
WHERE col1 = 100 AND col2 = 0

SET STATISTICS IO, TIME OFF
```

今度はどのパーティションを読みに行ったらいいのかわかるので論理読み取りの数が2になりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16609/9bf77187-f67b-0ec5-90e1-f7981429f867.png)

# パーティションを指定してTRUNCATEする

`WITH(PARTITIONS(パーティション番号))`で指定します。

```sql
TRUNCATE TABLE PartitionTable WITH(PARTITIONS(1))
```

パーティション番号 = 1の件数がゼロになっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16609/5dfce666-53ea-4003-894d-0a438e645e11.png)

# パーティションを追加する

次に使うスキーマを指定したあと、境界になる値を指定します。

```sql
ALTER PARTITION SCHEME partitionScheme1 NEXT USED 'PRIMARY'
ALTER PARTITION FUNCTION partitionFunction() SPLIT RANGE (10)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16609/cb00118b-760c-193a-de33-1e82b6c72550.png)

# パーティションをマージする

境界値として消したい値を指定します。

```sql
ALTER PARTITION FUNCTION partitionFunction() MERGE RANGE (0)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16609/d4759f9b-f680-912d-2ef8-bf69c48ee75f.png)

動きが分かるように値が入っているところをマージしてみます。

```sql
ALTER PARTITION FUNCTION partitionFunction() MERGE RANGE (4)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16609/cfb4845d-c79e-7def-da0e-b5b5d40081c1.png)

もう一度分割してみます。

```sql
ALTER PARTITION SCHEME partitionScheme1 NEXT USED 'PRIMARY'
ALTER PARTITION FUNCTION partitionFunction() SPLIT RANGE (4)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16609/02738932-1727-dfd4-fbf0-8fbe0b1932a8.png)


# シリーズ SQL Server/SQL Database再入門

なんとなくならSQL Serverを使えている人のためのSQL Server/SQL DatabaseのTips集です。

1. [Decimal型とMoney型の違い](https://qiita.com/yyukawa/items/c1e23a97d4170edba23f)
1. [Index SeekとIndex Scan](https://qiita.com/yyukawa/items/4cc1cd5e447b5a5d1b48)
1. [クエリ毎の性能指標の確認方法を知る](https://qiita.com/yyukawa/items/c80151bb9db98de491a2)
1. [SQL Databaseでパーティション テーブルとパーティション インデックス](https://qiita.com/yyukawa/items/902a166c2520793b8116#%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA-sql-serversql-database%E5%86%8D%E5%85%A5%E9%96%80)
1. [統計とクエリの関係](https://qiita.com/yyukawa/items/f05b8a2b89e955c94299)
1. [結合方法：NESTED LOOP、MERGE、HASH](https://qiita.com/yyukawa/items/706291047576a654f1e1#merge-join-3)