仮想テーブルを作る - CREATE VIEW / MATERIALIZED VIEW
通常ビューとマテリアライズドビューの違い、更新可能ビューの条件、リフレッシュ戦略
概念図
構文
sql
CREATE [MATERIALIZED] VIEW name AS SELECT ... ;サンプル
通常ビューは問い合わせに展開されるだけ。「名前付きの SELECT」として使える
sql
-- 通常ビュー: 定義を保存するだけで実体は持たない
CREATE VIEW v_active_users AS
SELECT id, email, name
FROM users
WHERE status = 'active';
-- 使うときは普通のテーブルのように
SELECT * FROM v_active_users WHERE name LIKE 'a%';通常ビューの仕組み
通常の VIEW は SQL 文の別名にすぎません。ビューに対する SELECT は、定義された SELECT に置き換えられて実行されます。実体のデータは持たないので、常に最新の結果が返り、ストレージは消費しません。
用途: 複雑な結合の再利用、権限分離(センシティブ列を隠す)、ドメインレベルの語彙として公開など。
マテリアライズドビュー (PostgreSQL / Oracle / SQL Server)
MATERIALIZED VIEW は問い合わせ結果を実体として保存するビューです。重い集計を事前計算しておく「キャッシュテーブル」のようなもの。更新には明示的なリフレッシュが必要です。
- PostgreSQL:
CREATE MATERIALIZED VIEW/REFRESH MATERIALIZED VIEW [CONCURRENTLY] - Oracle: 古くからサポート、ON COMMIT / ON DEMAND などリフレッシュモード豊富
- SQL Server: 「インデックス付きビュー」として実装
- MySQL: 標準サポートなし。サマリーテーブル + トリガー / バッチで代替
sql
-- PostgreSQL
CREATE MATERIALIZED VIEW mv_daily_sales AS
SELECT DATE_TRUNC('day', placed_at) AS day,
SUM(amount) AS total
FROM orders
GROUP BY 1;
-- 読み取りロックを最小限にリフレッシュ
REFRESH MATERIALIZED VIEW CONCURRENTLY mv_daily_sales;更新可能ビュー (Updatable View)
一部のビューはそのまま INSERT / UPDATE / DELETE ができます。これを更新可能ビューと呼びます。成立条件は RDBMS で異なりますが、おおむね共通なのは:
- 単一テーブルからの SELECT である
DISTINCT、GROUP BY、集約関数、UNION、ウィンドウ関数を含まない- すべての非 NULL・非 DEFAULT 列がビューに含まれている(INSERT 時)
複雑なビューを更新可能にしたい場合、PostgreSQL の INSTEAD OF トリガーや、SQL Server の INSTEAD OF トリガーで手動定義できます。
WITH CHECK OPTION
更新可能ビューに WITH CHECK OPTION を付けると、ビューの定義条件から外れる更新を拒否できます。たとえば「active ユーザーだけ見えるビュー」に inactive を混ぜる UPDATE を防げます。
sql
CREATE VIEW v_active_users AS
SELECT * FROM users WHERE status = 'active'
WITH CHECK OPTION;
-- 以下は失敗する(status が 'active' から外れるため)
UPDATE v_active_users SET status = 'suspended' WHERE id = 1;落とし穴
- ビューの多段化: ビューからビューを参照するとプランナが展開を諦めて遅くなることがある。深くなりすぎたら素の SQL か MV に
- MATERIALIZED VIEW のリフレッシュ忘れ: 古いデータが出続ける。スケジューラーや更新トリガーを忘れずに
- REFRESH(非 CONCURRENTLY)は書き込みロック: PostgreSQL で普通の
REFRESHは対象を排他ロックする。CONCURRENTLYを使うには UNIQUE インデックスが必須 - 権限設計とのズレ: ビュー経由の権限と元テーブルの権限が噛み合わずアクセス拒否になる
