REFRESH MATERIALIZED VIEWの小さな改善

# はじめに
にゃーん
この記事は、[PostgreSQL 10全部ぬこ Advent Calendar 2017](https://qiita.com/advent-calendar/2017/pg10-nuko-only) の22日目のエントリです。
今日は短め。

# 今日のお題
今日のお題は、```REFRESH MATERIALIZED VIEW```のちょっとした改善の話。

# PostgreSQL 10での改善点
PostgreSQLの稼働統計情報ビューの```pg_stat_user_tables```は、テーブルだけではなく、MATERIALIZED VIEWも対象になっている。
しかし、PostgreSQL 9.6までは、```REFRESH MATERIALIZED VIEW```が実施されたときに、pg_stas_user_tablesの内容が更新されないままになっていた。

たとえば、以下のような簡単なMATERIALIZED VIEWを定義しておく。

```postgres-console
matview=# CREATE TABLE test_t (id int primary key, data int);
CREATE TABLE
matview=# CREATE MATERIALIZED VIEW test_mv AS SELECT * FROM test_t;
SELECT 0
matview=# 
```

```postgres-console
matview=# \d+ test_t
                        Table "public.test_t"
 Column |  Type   | Modifiers | Storage | Stats target | Description 
--------+---------+-----------+---------+--------------+-------------
 id     | integer | not null  | plain   |              | 
 data   | integer |           | plain   |              | 
Indexes:
    "test_t_pkey" PRIMARY KEY, btree (id)

matview=# \d+ test_mv
                 Materialized view "public.test_mv"
 Column |  Type   | Modifiers | Storage | Stats target | Description 
--------+---------+-----------+---------+--------------+-------------
 id     | integer |           | plain   |              | 
 data   | integer |           | plain   |              | 
View definition:
 SELECT test_t.id,
    test_t.data
   FROM test_t;
```

この状態で、pg_stat_user_tablesを確認する。

```postgres-console
matview=# SELECT relname, n_tup_ins, n_tup_upd, n_tup_del, n_live_tup, n_dead_tup  FROM pg_stat_user_tables ;
 relname | n_tup_ins | n_tup_upd | n_tup_del | n_live_tup | n_dead_tup 
---------+-----------+-----------+-----------+------------+------------
 test_t  |         0 |         0 |         0 |          0 |          0
 test_mv |         0 |         0 |         0 |          0 |          0
(2 rows)
```
この時点では、何も更新していないのでこれらの値は全て0だ。

さて、test_mv の元になった test_t テーブルに対して、以下のようにINSERT/UPDATE/DELETE文を実行する。

```postgres-console
matview=# INSERT INTO test_t VALUES (generate_series(1, 100000), (random() * 100000)::int);
INSERT 0 100000
matview=# UPDATE test_t SET data = -1 WHERE id <= 10;
UPDATE 10
matview=# DELETE FROM test_t WHERE id >= 99990;
DELETE 11
matview=#
```

この状態で、pg_stat_user_tablesを参照する。

```postgres-console
matview=# SELECT relname, n_tup_ins, n_tup_upd, n_tup_del, n_live_tup, n_dead_tup  FROM pg_stat_user_tables ;
 relname | n_tup_ins | n_tup_upd | n_tup_del | n_live_tup | n_dead_tup 
---------+-----------+-----------+-----------+------------+------------
 test_t  |    100000 |        10 |        11 |      99989 |         21
 test_mv |         0 |         0 |         0 |          0 |          0
(2 rows)
```
test_tテーブルの値は更新されている。
ここまでは、PostgreSQL 9.6までも、PostgreSQL 10でも変わりない。

## PostgreSQL 9.6まで
ここで```REFRESH MATERIALIZED VIEW```コマンドを実行して、```pg_stat_user_tables ```を参照する。

```postgres-console
matview=# REFRESH MATERIALIZED VIEW test_mv ;
REFRESH MATERIALIZED VIEW
matview=# SELECT relname, n_tup_ins, n_tup_upd, n_tup_del, n_live_tup, n_dead_tup  FROM pg_stat_user_tables ;
 relname | n_tup_ins | n_tup_upd | n_tup_del | n_live_tup | n_dead_tup 
---------+-----------+-----------+-----------+------------+------------
 test_t  |    100000 |        10 |        11 |      99989 |         21
 test_mv |         0 |         0 |         0 |          0 |          0
(2 rows)
```
PostgreSQL 9.6までは、```REFRESH MATERIALIZED VIEW```を実行しても対象のMATERIALIZED VIEWの稼働統計情報ビューの内容は更新されなかった。

## PostgreSQL 10から
PostgreSQL 10で同じ操作を実施してみる。

```postgres-console
matview=# REFRESH MATERIALIZED VIEW test_mv ;
REFRESH MATERIALIZED VIEW
matview=# SELECT relname, n_tup_ins, n_tup_upd, n_tup_del, n_live_tup, n_dead_tup  FROM pg_stat_user_tables ;
 relname | n_tup_ins | n_tup_upd | n_tup_del | n_live_tup | n_dead_tup 
---------+-----------+-----------+-----------+------------+------------
 test_t  |    100000 |        10 |        11 |      99989 |         21
 test_mv |     99989 |         0 |         0 |      99989 |          0
(2 rows)
```
```REFRESH MATERIALIZED VIEW```後に、MATERIALIZED VIEWのn_tup_insの内容、n_live_tupの値が更新されているのがわかる。

# おわりに
実は盲点というか、こんな問題がPostgreSQL 9.6まではあったんだなー、という印象だ。
でも、こういう細かい改善も大事だよなぁ。

# 参考：該当するリリースノート
本エントリに関連するPostgreSQL 10リリースノートの記載です。

E.2.3.1.6. Monitoring

> * Properly update the statistics collector during REFRESH MATERIALIZED VIEW (Jim Mlodgenski)