Query Go
仮想テーブルを作る - CREATE VIEW / MATERIALIZED VIEW の使い方・オプション・サンプル

仮想テーブルを作る - CREATE VIEW / MATERIALIZED VIEW

通常ビューとマテリアライズドビューの違い、更新可能ビューの条件、リフレッシュ戦略

概念図

CREATE VIEW / MATERIALIZED VIEW diagram

構文

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%';

通常ビューの仕組み

通常の VIEWSQL 文の別名にすぎません。ビューに対する 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 である
  • DISTINCTGROUP 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 インデックスが必須
  • 権限設計とのズレ: ビュー経由の権限と元テーブルの権限が噛み合わずアクセス拒否になる

関連トピック