はじめに
アプリケーション開発において、複雑なSQLクエリを何度も書くことに疲れた経験はありませんか。あるいは、重たい集計クエリの結果を毎回計算するのではなく、キャッシュしておきたいと思ったことはないでしょうか。
PostgreSQLのビュー(View)とマテリアライズドビュー(Materialized View)は、これらの課題を解決する強力な機能です。ビューを使えば複雑なクエリを名前付きで再利用でき、マテリアライズドビューを使えば計算結果を物理的に保存してパフォーマンスを大幅に向上できます。
本記事では、ビューの基本概念と作成方法、更新可能ビューの条件と活用法、マテリアライズドビューの仕組みとリフレッシュ戦略、そしてパフォーマンス面でのトレードオフについて、実践的なサンプルコードとともに解説します。
この記事を読むことで、以下のことができるようになります。
- ビューを使って複雑なクエリを抽象化し、再利用可能な形で管理できる
- 更新可能ビューの条件を理解し、適切に設計できる
- マテリアライズドビューで集計結果をキャッシュし、パフォーマンスを最適化できる
- リフレッシュ戦略を設計し、データの鮮度とパフォーマンスのバランスを取れる
- ビューとマテリアライズドビューの使い分けを判断できる
前提条件
- PostgreSQL 14以降がインストールされていること
- psqlまたはGUIツール(pgAdmin、DBeaver等)でデータベースに接続できること
- SELECT文、JOINの基本的な構文を理解していること
基本的なSELECT文やJOINについては、以下の関連記事を参照してください。
サンプルデータの準備
本記事で使用するサンプルテーブルとデータを準備します。ECサイトの売上分析を想定したデータ構成です。
|
|
ビューの基本
ビューとは
ビュー(View)とは、SELECT文に名前を付けて保存したものです。ビュー自体はデータを持たず、参照されるたびに定義されたクエリが実行されます。これにより、複雑なクエリを抽象化し、シンプルなインターフェースとして提供できます。
ビューの主な特徴は以下の通りです。
| 特徴 | 説明 |
|---|---|
| クエリの抽象化 | 複雑なJOINや計算を隠蔽し、シンプルなSELECTで利用可能 |
| 再利用性 | 同じクエリロジックを複数箇所で使い回せる |
| セキュリティ | 特定のカラムや行だけを公開できる |
| 保守性 | クエリの変更がビュー定義の修正だけで済む |
| リアルタイム性 | 参照時に常に最新のデータを取得 |
ビューの内部動作を図で表すと以下のようになります。
flowchart LR
A[アプリケーション] -->|SELECT * FROM view| B[ビュー]
B -->|クエリ展開| C[元のSELECT文]
C --> D[(テーブル1)]
C --> E[(テーブル2)]
D --> F[結果セット]
E --> F
F --> ACREATE VIEWの基本構文
ビューはCREATE VIEW文で作成します。基本的な構文は以下の通りです。
|
|
商品と売上に関する情報を結合したビューを作成してみましょう。
|
|
作成したビューは通常のテーブルと同様にSELECTで参照できます。
|
|
実行結果は以下の通りです。
product_id | product_name | category_name | price | cost | profit_margin
------------+----------------------+----------------+--------+--------+---------------
1 | ノートPC | PC周辺機器 | 120000 | 80000 | 40000
2 | ワイヤレスマウス | PC周辺機器 | 3500 | 1500 | 2000
3 | メカニカルキーボード | PC周辺機器 | 15000 | 8000 | 7000
...
ビューの活用シーン
ビューは様々なシーンで活用できます。代表的なユースケースを見ていきましょう。
複雑なクエリの簡略化
複数のテーブルをJOINする複雑なクエリを、シンプルなビューとして提供できます。
|
|
これにより、アプリケーション側では以下のようなシンプルなクエリで必要な情報を取得できます。
|
|
セキュリティ目的での行・列の制限
機密情報を含むテーブルから、必要な情報だけを公開するビューを作成できます。
|
|
計算フィールドの事前定義
計算ロジックをビューに含めることで、アプリケーション側の実装を簡略化できます。
|
|
ビューの管理
ビューの定義変更や削除の方法を確認しましょう。
ビューの置き換え
CREATE OR REPLACE VIEWを使うと、既存のビューを削除せずに定義を変更できます。ただし、新しい定義は同じカラム名・型・順序を持つ必要があります(末尾にカラムを追加することは可能)。
|
|
ビューの削除
ビューはDROP VIEWで削除します。
|
|
ビュー定義の確認
ビューの定義を確認するには、\dコマンドやpg_viewsカタログを使用します。
|
|
更新可能ビュー
更新可能ビューとは
PostgreSQLでは、一定の条件を満たすビューに対してINSERT、UPDATE、DELETE操作を実行できます。これを「更新可能ビュー(Updatable View)」と呼びます。更新可能ビューに対する操作は、自動的に元のテーブルに反映されます。
更新可能ビューの条件は以下の通りです。
| 条件 | 説明 |
|---|---|
| 単一テーブル | FROM句に含まれるテーブルが1つのみ |
| 集計なし | GROUP BY、HAVING、集計関数を使用していない |
| DISTINCT なし | DISTINCT句を使用していない |
| UNION なし | UNION、INTERSECT、EXCEPTを使用していない |
| LIMIT/OFFSET なし | トップレベルでLIMIT、OFFSET を使用していない |
| WITH なし | WITH句(CTE)を使用していない |
更新可能ビューの作成例
条件を満たすシンプルなビューを作成し、更新操作を試してみましょう。
|
|
このビューは単一テーブルから作成されており、更新可能ビューの条件を満たしています。
|
|
重要な点として、ビューの条件(WHERE prefecture = '東京都')を満たさないデータもINSERTできてしまいます。これを防ぐにはCHECK OPTIONを使用します。
WITH CHECK OPTIONによる制約
WITH CHECK OPTIONを指定すると、ビューの条件を満たさないデータの挿入・更新を防止できます。
|
|
このビューに対して条件を満たさないデータを挿入しようとするとエラーになります。
|
|
CHECK OPTIONには2つのモードがあります。
| モード | 説明 |
|---|---|
| LOCAL | そのビューの条件のみチェック |
| CASCADED | 依存するすべてのビューの条件もチェック(デフォルト) |
INSTEAD OF トリガーによる複雑なビューの更新
更新可能ビューの条件を満たさない複雑なビューでも、INSTEAD OFトリガーを使えば更新操作を実装できます。
|
|
これにより、JOINを含むビューに対してもUPDATE操作が可能になります。
|
|
マテリアライズドビューの基本
マテリアライズドビューとは
マテリアライズドビュー(Materialized View)は、ビューのクエリ結果を物理的に保存するオブジェクトです。通常のビューと異なり、クエリの実行結果がテーブルのように永続化されるため、参照時に再計算が不要になります。
ビューとマテリアライズドビューの違いを比較してみましょう。
| 特性 | ビュー | マテリアライズドビュー |
|---|---|---|
| データの保存 | 保存しない(参照時に計算) | 保存する(事前に計算) |
| 参照時の速度 | クエリ実行時間に依存 | 高速(保存済みデータを返す) |
| データの鮮度 | 常に最新 | リフレッシュまで古いまま |
| ストレージ使用量 | なし | 結果セット分必要 |
| インデックス | 作成不可 | 作成可能 |
| 更新操作 | 条件付きで可能 | 不可(リフレッシュのみ) |
flowchart LR
subgraph ビュー
A1[クエリ実行] --> B1[テーブル参照] --> C1[結果生成]
end
subgraph マテリアライズドビュー
A2[リフレッシュ] --> B2[テーブル参照] --> C2[結果保存]
D2[クエリ実行] --> C2 --> E2[保存済み結果を返す]
endCREATE MATERIALIZED VIEWの構文
マテリアライズドビューはCREATE MATERIALIZED VIEWで作成します。
|
|
月別売上サマリのマテリアライズドビューを作成してみましょう。
|
|
作成直後から参照可能です。
|
|
実行結果は以下の通りです。
sales_month | order_count | customer_count | total_quantity | total_revenue | total_profit | avg_order_amount
-------------+-------------+----------------+----------------+---------------+--------------+------------------
2025-10-01 | 2 | 2 | 4 | 173500 | 60500 | 86750
2025-11-01 | 3 | 3 | 4 | 218000 | 77000 | 72667
2025-12-01 | 2 | 2 | 3 | 138500 | 49000 | 69250
WITH NO DATAオプション
WITH NO DATAを指定すると、マテリアライズドビューの定義のみを作成し、データは後からリフレッシュで投入できます。
|
|
マテリアライズドビューへのインデックス作成
マテリアライズドビューはテーブルと同様にインデックスを作成できます。これにより、参照時のパフォーマンスをさらに向上させることが可能です。
|
|
リフレッシュ戦略
REFRESH MATERIALIZED VIEWの基本
マテリアライズドビューのデータは自動更新されません。データを最新化するにはREFRESH MATERIALIZED VIEWを実行します。
|
|
リフレッシュ中は、デフォルトでマテリアライズドビューに対するSELECTがブロックされます。
CONCURRENTLYオプション
CONCURRENTLYオプションを使用すると、リフレッシュ中もSELECTが可能になります。ただし、使用には条件があります。
|
|
CONCURRENTLYを使用するための条件は以下の通りです。
| 条件 | 説明 |
|---|---|
| ユニークインデックス必須 | すべての行を一意に識別できるインデックスが必要 |
| データが存在 | WITH NO DATAで作成した直後は使用不可 |
| 単一リフレッシュ | 同時に複数のCONCURRENTLYは実行不可 |
CONCURRENTLYの動作を図で表すと以下のようになります。
sequenceDiagram
participant App as アプリケーション
participant MV as マテリアライズドビュー
participant New as 新データ(一時)
Note over MV: 通常リフレッシュ
App->>MV: SELECT(ブロック)
MV-->>App: 待機...
Note over MV,New: CONCURRENTLYリフレッシュ
App->>MV: SELECT
MV-->>App: 古いデータを返す
New->>New: 新データ計算
New->>MV: 差分マージ
App->>MV: SELECT
MV-->>App: 新しいデータを返す自動リフレッシュの実装パターン
PostgreSQLにはマテリアライズドビューの自動リフレッシュ機能が組み込まれていません。以下のパターンで実装できます。
pg_cronによる定期実行
pg_cron拡張を使用して、定期的にリフレッシュを実行できます。
|
|
トリガーによるイベント駆動リフレッシュ
元テーブルの更新時にリフレッシュを実行するトリガーを設定することも可能です。ただし、頻繁な更新がある場合はパフォーマンスに影響するため注意が必要です。
|
|
リフレッシュ管理テーブルによる制御
リフレッシュ履歴を管理し、一定時間経過後のみリフレッシュを実行するパターンです。
|
|
パフォーマンスとトレードオフ
ビューのパフォーマンス特性
ビューは参照時に毎回クエリが実行されるため、元のクエリと同じパフォーマンス特性を持ちます。複雑なJOINや集計を含むビューは、参照のたびにその処理が実行されます。
EXPLAIN ANALYZEでビューのパフォーマンスを確認してみましょう。
|
|
実行計画を見ると、ビューの定義クエリがそのまま展開されて実行されていることがわかります。
ビューのパフォーマンスを改善するためのポイントは以下の通りです。
- 元テーブルに適切なインデックスを作成する
- 不要なカラムの取得を避ける
- WHERE句で絞り込みを行う
マテリアライズドビューのパフォーマンス特性
マテリアライズドビューは事前計算された結果を返すため、参照時は非常に高速です。
|
|
マテリアライズドビューは単純なテーブルスキャンで済むため、複雑な集計クエリよりも大幅に高速です。
ストレージとリフレッシュコストのトレードオフ
マテリアライズドビューを使用する際は、以下のトレードオフを考慮する必要があります。
| 要素 | ビュー | マテリアライズドビュー |
|---|---|---|
| 参照速度 | 遅い(クエリ実行) | 速い(保存データ返却) |
| データ鮮度 | 常に最新 | リフレッシュ依存 |
| ストレージ | 不要 | 結果セット分必要 |
| リフレッシュコスト | なし | 完全再計算が必要 |
| インデックス | 不可 | 可能 |
graph TD
A[データ更新頻度は?] -->|高い| B[データの鮮度は重要?]
A -->|低い| C[マテリアライズドビュー推奨]
B -->|リアルタイム必須| D[ビュー推奨]
B -->|多少の遅延OK| E[参照頻度は?]
E -->|高い| F[マテリアライズドビュー + 頻繁なリフレッシュ]
E -->|低い| D
C --> G[CONCURRENTLY + 定期リフレッシュ]使い分けの指針
ビューとマテリアライズドビューの選択指針をまとめます。
ビューを選択するケース
- データの鮮度が最優先
- 元テーブルが頻繁に更新される
- クエリが十分に高速(インデックスが効いている)
- 更新可能ビューが必要
マテリアライズドビューを選択するケース
- 集計やJOINが複雑で実行に時間がかかる
- 同じクエリが頻繁に実行される
- データの鮮度に多少の遅延が許容される
- ダッシュボードやレポート用途
併用パターン
実際のシステムでは、両者を組み合わせて使用することが効果的です。
|
|
実践的な活用例
ダッシュボード向けサマリビュー
経営ダッシュボード向けに、複数の指標を1つのマテリアライズドビューにまとめます。
|
|
階層データの展開ビュー
再帰CTEを使用して、カテゴリの階層構造を展開するビューを作成します。
|
|
このビューを使用することで、フラットなカテゴリテーブルから階層構造を簡単に取得できます。
|
|
実行結果は以下の通りです。
category_id | name | parent_id | full_path | level | path_ids
-------------+--------------------+-----------+------------------------------+-------+------------
1 | 電子機器 | | 電子機器 | 1 | {1}
4 | PC周辺機器 | 1 | 電子機器 > PC周辺機器 | 2 | {1,4}
5 | スマートフォン関連 | 1 | 電子機器 > スマートフォン関連| 2 | {1,5}
2 | 家具 | | 家具 | 1 | {2}
6 | オフィス家具 | 2 | 家具 > オフィス家具 | 2 | {2,6}
3 | 文具 | | 文具 | 1 | {3}
セキュリティ目的のビュー
行レベルセキュリティと組み合わせて、ユーザーごとに異なるデータを表示するビューを作成します。
|
|
まとめ
本記事では、PostgreSQLのビューとマテリアライズドビューについて、基本概念から実践的な活用方法まで解説しました。
ビューとマテリアライズドビューのポイントを整理すると以下の通りです。
| 機能 | ビュー | マテリアライズドビュー |
|---|---|---|
| 主な用途 | クエリの抽象化、セキュリティ | パフォーマンス最適化、キャッシュ |
| データ保存 | なし | あり |
| 更新操作 | 条件付きで可能 | 不可(リフレッシュのみ) |
| 適用場面 | リアルタイムデータ参照 | 集計・分析クエリの高速化 |
ビューを使いこなすことで、複雑なSQLを再利用可能な形で管理でき、アプリケーションコードの保守性が向上します。マテリアライズドビューを活用することで、重たい集計クエリのパフォーマンスを大幅に改善できます。
実際のシステム設計では、データの鮮度要件、参照頻度、更新頻度を考慮して、適切な方を選択してください。多くの場合、両者を組み合わせることで最適なソリューションを実現できます。