PostgreSQL 17がやってくる(6) ネストしたパーティションの分割

# はじめに
にゃーん。
今回は小ネタ。
前回の記事（[PostgreSQL 17がやってくる(5) パーティションの分割＆マージ ](https://qiita.com/nuko_yokohama/items/12cd608f69dd197908f1)）でPostgreSQL 17で入るパーティションの分割とマージについて書いたけど、少し気になることがあったので追加で確認してみた。

# ネストしたパーティション
PostgrSQLのパーティションはねストすることができる。
例えば、

```text
test
+-- test_2023
+-- test_2024
|   +--- 2024_1q
|   +--- 2024_2q
|   +--- 2024_3q
|   +--- 2024_4q
+-- test_2025
```
のように、日付をレンジキーにして、``test_2023``,``test_2024``,``test_2025``のように分割し、更に``test_2024``も日付をレンジキーにして、``test_2024_1q``,``test_2024_2q``,``test_2024_3q``,``test_2024_4q``のようにネストしたパーティションを構成することができる。

psqlの``\d+``メタコマンドで見るとこんな感じ。
```sql
postgres@test=# \d+ test
                                      Partitioned table "public.test"
  Column  |  Type   | Collation | Nullable | Default | Storage  | Compression | Stats target | Description
----------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
 id       | integer |           |          |         | plain    |             |              |
 part_key | date    |           |          |         | plain    |             |              |
 data     | text    |           |          |         | extended |             |              |
Partition key: RANGE (part_key)
Partitions: test_2023 FOR VALUES FROM ('2023-01-01') TO ('2024-01-01'),
            test_2024 FOR VALUES FROM ('2024-01-01') TO ('2025-01-01'), PARTITIONED,
            test_2025 FOR VALUES FROM ('2025-01-01') TO ('2026-01-01'),
            test_default DEFAULT

postgres@test=# \d+ test_2024
                                   Partitioned table "public.test_2024"
  Column  |  Type   | Collation | Nullable | Default | Storage  | Compression | Stats target | Description
----------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
 id       | integer |           |          |         | plain    |             |              |
 part_key | date    |           |          |         | plain    |             |              |
 data     | text    |           |          |         | extended |             |              |
Partition of: test FOR VALUES FROM ('2024-01-01') TO ('2025-01-01')
Partition constraint: ((part_key IS NOT NULL) AND (part_key >= '2024-01-01'::date) AND (part_key < '2025-01-01'::date))
Partition key: RANGE (part_key)
Partitions: test_2024_1q FOR VALUES FROM ('2024-01-01') TO ('2024-04-01'),
            test_2024_2q FOR VALUES FROM ('2024-04-01') TO ('2024-07-01'),
            test_2024_3q FOR VALUES FROM ('2024-07-01') TO ('2024-10-01'),
            test_2024_4q FOR VALUES FROM ('2024-10-01') TO ('2025-01-01')

postgres@test=# 
```

# ネストしたパーティションの分割
## 検証時のバージョン
2024-05-10（commit 31e8f4e619d9b5856fa2bd5713cb1e2e170a9c7d）を使って検証した。

## 検証内容
最初に示した中間階層``test_2024``のパーティションを指定して、月単位に一気に分割できるか確認してみる。

```sql
$ psql test -ef split_year2month.sql
Null display is "(null)".
ALTER TABLE test_2024 SPLIT PARTITION test_2024 INTO
  (
    PARTITION test_01m FOR VALUES FROM ('2024-01-01') TO ('2024-02-01'),
    PARTITION test_02m FOR VALUES FROM ('2024-02-01') TO ('2024-03-01'),
    PARTITION test_03m FOR VALUES FROM ('2024-03-01') TO ('2024-04-01'),
    PARTITION test_01m FOR VALUES FROM ('2024-04-01') TO ('2024-05-01'),
    PARTITION test_02m FOR VALUES FROM ('2024-05-01') TO ('2024-06-01'),
    PARTITION test_03m FOR VALUES FROM ('2024-06-01') TO ('2024-07-01'),
    PARTITION test_01m FOR VALUES FROM ('2024-07-01') TO ('2024-08-01'),
    PARTITION test_02m FOR VALUES FROM ('2024-08-01') TO ('2024-09-01'),
    PARTITION test_03m FOR VALUES FROM ('2024-09-01') TO ('2024-10-01'),
    PARTITION test_01m FOR VALUES FROM ('2024-10-01') TO ('2024-11-01'),
    PARTITION test_02m FOR VALUES FROM ('2024-11-01') TO ('2024-12-01'),
    PARTITION test_03m FOR VALUES FROM ('2024-12-01') TO ('2025-01-01')
  );
psql:split_year2month.sql:15: ERROR:  "test_2024" is not a table
$
```
**ダメでした。**

分割対象の``test_2024``を指定したけど、``not a table``と怒られてしまう。
いや、``test_2024``もPARTITIONEDではあるけどtableじゃないの？と一瞬思ったけど、たぶん**実際にデータが格納されている末端のテーブルじゃない**からダメなのかも。
（PARTITIONEDの種別がついているとダメっぽい）

なので、パーティションを分割する場合には、``SPLIT``の対象には、実際にデータが格納されている末端の階層のパーティションを指定しないとダメっぽい。

今回の場合だと、``test_2024``を分割した末端の階層（``test_2024_1q``,``test_2024_2q``,``test_2024_3q``,``test_2024_4q``）ごとに``ALTER TABLE ... SPLIT``コマンドを発行する必要がある。

```sql
$ psql test -ef split_quarter2month.sql
Null display is "(null)".
ALTER TABLE test_2024 SPLIT PARTITION test_2024_1q INTO
  (
    PARTITION test_2024_01m FOR VALUES FROM ('2024-01-01') TO ('2024-02-01'),
    PARTITION test_2024_02m FOR VALUES FROM ('2024-02-01') TO ('2024-03-01'),
    PARTITION test_2024_03m FOR VALUES FROM ('2024-03-01') TO ('2024-04-01')
  );
ALTER TABLE
ALTER TABLE test_2024 SPLIT PARTITION test_2024_2q INTO
  (
    PARTITION test_2024_04m FOR VALUES FROM ('2024-04-01') TO ('2024-05-01'),
    PARTITION test_2024_05m FOR VALUES FROM ('2024-05-01') TO ('2024-06-01'),
    PARTITION test_2024_06m FOR VALUES FROM ('2024-06-01') TO ('2024-07-01')
  );
ALTER TABLE
ALTER TABLE test_2024 SPLIT PARTITION test_2024_3q INTO
  (
    PARTITION test_2024_07m FOR VALUES FROM ('2024-07-01') TO ('2024-08-01'),
    PARTITION test_2024_08m FOR VALUES FROM ('2024-08-01') TO ('2024-09-01'),
    PARTITION test_2024_09m FOR VALUES FROM ('2024-09-01') TO ('2024-10-01')
  );
ALTER TABLE
ALTER TABLE test_2024 SPLIT PARTITION test_2024_4q INTO
  (
    PARTITION test_2024_10m FOR VALUES FROM ('2024-10-01') TO ('2024-11-01'),
    PARTITION test_2024_11m FOR VALUES FROM ('2024-11-01') TO ('2024-12-01'),
    PARTITION test_2024_12m FOR VALUES FROM ('2024-12-01') TO ('2025-01-01')
  );
ALTER TABLE
$
```

# おわりに
``ALTER TABLE ... SPLIT``コマンドでは、中間の階層のパーティションを指定して、一気に分割はできないことがわかった。
複数段のパーティションテーブルを設計する場合の注意点が分かったのでヨシ！