Oracle のビューは、複雑な SQL を名前を付けて保存できる「仮想テーブル」です。よく使う JOIN クエリをビューにしておけば、毎回複雑な SQL を書かずに済みます。
また、テーブルに直接アクセスさせず、ビュー越しにだけアクセスさせることで列・行レベルのアクセス制御が実現できます。これは Oracle の権限設定だけでは難しい細かいセキュリティ制御に有効です。
一方で、ビューへの INSERT / UPDATE / DELETE には制約があり、WITH CHECK OPTION と WITH READ ONLY の使い分けを知らないと意図しないデータ変更を許してしまうことがあります。
この記事では CREATE VIEW の全構文から、更新可能ビューの条件・WITH CHECK OPTION の動作・セキュリティ目的のビュー設計まで、実務で使う知識を体系的に解説します。
・CREATE VIEW の完全な構文と全オプション
・シンプルビュー・複合ビュー・FORCE ビューの違い
・ビューへの INSERT / UPDATE / DELETE が可能な条件と不可能な条件
・WITH CHECK OPTION で更新内容をビューの条件に制限する方法
・WITH READ ONLY で参照専用ビューを作る方法
・CREATE OR REPLACE VIEW で定義を上書き変更する方法
・セキュリティ目的のアクセス制御ビューの設計パターン
・ビューのパフォーマンスへの影響と注意点
Oracle のビューとは
ビューは、SELECT 文に名前を付けて保存したデータベースオブジェクトです。物理的なデータは持たず、ビューを参照するたびに定義された SELECT が実行されます。
| 特徴 | 説明 |
|---|---|
| 仮想テーブル | データを物理的に持たない。参照時に SELECT が実行される |
| テーブルと同様に扱える | SELECT だけでなく(条件付きで)INSERT / UPDATE / DELETE も可能 |
| 複雑な SQL の再利用 | 何度も使う JOIN や集計 SQL を名前を付けて保存できる |
| アクセス制御 | 特定の列・行だけを見せるセキュリティレイヤーとして使える |
| 依存関係 | ベーステーブルが変更・削除されると INVALID になる |
CREATE VIEW の基本構文
CREATE [OR REPLACE] [FORCE | NOFORCE] VIEW ビュー名
[(列別名1, 列別名2, ...)]
AS
SELECT 文
[WITH CHECK OPTION [CONSTRAINT 制約名]]
[WITH READ ONLY];
-- IT 部門の社員だけを表示するビュー CREATE VIEW v_emp_it AS SELECT emp_id, emp_name, hire_date, salary FROM emp WHERE dept_id = 10;
-- 列に日本語の別名を付けてビューを作成
CREATE VIEW v_emp_summary (
社員ID, 氏名, 部署名, 給与
) AS
SELECT e.emp_id, e.emp_name, d.dept_name, e.salary
FROM emp e
JOIN dept d ON e.dept_id = d.dept_id;
ビューの種類
シンプルビュー(1テーブル・更新可能)
単一テーブルから SELECT するビューです。後述の条件を満たす場合、INSERT / UPDATE / DELETE も可能です。
CREATE VIEW v_emp_active AS SELECT emp_id, emp_name, dept_id, salary FROM emp WHERE status = 'ACTIVE';
複合ビュー(JOIN・集計あり・通常は更新不可)
複数テーブルの JOIN、GROUP BY、集計関数、DISTINCT などを含むビューです。通常は参照専用になります。
CREATE VIEW v_emp_with_dept AS
SELECT
e.emp_id,
e.emp_name,
e.salary,
d.dept_name,
d.dept_id
FROM emp e
JOIN dept d ON e.dept_id = d.dept_id;
CREATE VIEW v_dept_stats AS
SELECT
d.dept_name,
COUNT(e.emp_id) AS emp_count,
AVG(e.salary) AS avg_salary,
MAX(e.salary) AS max_salary
FROM dept d
LEFT JOIN emp e ON d.dept_id = e.dept_id
GROUP BY d.dept_name;
FORCE ビュー(ベーステーブルがなくても作成できる)
通常、参照先のテーブルやビューが存在しない状態でビューを作ろうとするとエラーになります。FORCE オプションを使うと、参照先が存在しなくてもコンパイルエラーを出さずにビューを作成できます。テーブルより先にビューを定義したい場合などに使います。ただし、FORCE で作成されたビューは参照先が存在するまで INVALID 状態になります。
-- まだ存在しない future_table を参照するビューを先に作成 CREATE FORCE VIEW v_future AS SELECT id, name FROM future_table; -- → 作成はできるが INVALID 状態。future_table 作成後に有効になる
ビューの定義を変更する(CREATE OR REPLACE VIEW)
ビューを一度 DROP して再作成する代わりに、CREATE OR REPLACE VIEW で定義を上書きできます。DROP しないため、このビューへの GRANT(権限付与)がそのまま維持されます。
-- 既存の v_emp_it ビューの定義を変更する CREATE OR REPLACE VIEW v_emp_it AS SELECT emp_id, emp_name, hire_date, salary, phone FROM emp WHERE dept_id = 10 AND status = 'ACTIVE';
・
DROP VIEW → CREATE VIEW:このビューへの GRANT が消える・
CREATE OR REPLACE VIEW:GRANT は維持される権限を付与したビューの定義を変更する場合は 必ず
OR REPLACE を使うことを推奨します。ビューへの INSERT / UPDATE / DELETE
シンプルビューであれば INSERT / UPDATE / DELETE が可能ですが、一定の条件を満たさないと DML は許可されません。
更新可能ビューの条件
| 条件 | INSERT | UPDATE | DELETE |
|---|---|---|---|
| 単一テーブルのビュー | ◯ | ◯ | ◯ |
| JOIN を含む(結合ビュー) | △(キー保存テーブルのみ) | △(キー保存テーブルのみ) | ✕ |
| GROUP BY / HAVING を含む | ✕ | ✕ | ✕ |
| DISTINCT を含む | ✕ | ✕ | ✕ |
| 集計関数(SUM / COUNT / MAX 等)を含む | ✕ | ✕ | ✕ |
| ROWNUM / ROWID を含む | ✕ | ✕ | ✕ |
| サブクエリを含む | ✕(場合による) | ✕(場合による) | ✕(場合による) |
-- ビューを通して UPDATE UPDATE v_emp_active SET salary = salary * 1.1 WHERE emp_id = 100; -- ビューを通して INSERT -- ※ ビュー定義の WHERE 条件(status = 'ACTIVE')を満たさない値でも INSERT できる(後述) INSERT INTO v_emp_active (emp_id, emp_name, dept_id, salary) VALUES (999, '新入社員', 10, 300000); -- ビューを通して DELETE DELETE FROM v_emp_active WHERE emp_id = 999;
WITH CHECK OPTION:更新内容をビューの条件に制限する
デフォルトでは、ビューを通じてビューの WHERE 条件を満たさないデータも INSERT / UPDATE できてしまいます。WITH CHECK OPTION を付けると、ビューの条件を満たさない DML は拒否されます。
-- status = 'ACTIVE' の社員のみを扱うビュー(CHECK OPTION付き) CREATE OR REPLACE VIEW v_emp_active AS SELECT emp_id, emp_name, dept_id, salary, status FROM emp WHERE status = 'ACTIVE' WITH CHECK OPTION CONSTRAINT ck_v_emp_active;
-- OK: status = 'ACTIVE' なので許可される INSERT INTO v_emp_active (emp_id, emp_name, dept_id, salary, status) VALUES (1001, '山田太郎', 10, 300000, 'ACTIVE'); -- NG: status = 'INACTIVE' はビューの条件を満たさない → ORA-01402 エラー INSERT INTO v_emp_active (emp_id, emp_name, dept_id, salary, status) VALUES (1002, '鈴木花子', 10, 300000, 'INACTIVE'); -- ORA-01402: ビューのWITH CHECK OPTIONの条件に違反しました
| 設定 | ビュー条件を外れる INSERT | ビュー条件を外れる UPDATE | 用途 |
|---|---|---|---|
| なし(デフォルト) | 可(ビューから見えなくなる) | 可(ビューから消える) | 柔軟に使いたい場合 |
| WITH CHECK OPTION | ORA-01402 エラー | ORA-01402 エラー | ビューの条件を外れるDMLを禁止したい場合 |
WITH READ ONLY:参照専用ビューにする
WITH READ ONLY を指定すると、ビューを通じた INSERT / UPDATE / DELETE をすべて禁止できます。参照目的のビューに意図しない更新が行われるのを防ぎます。
-- 集計ビューを参照専用に
CREATE OR REPLACE VIEW v_dept_stats AS
SELECT
d.dept_name,
COUNT(e.emp_id) AS emp_count,
AVG(e.salary) AS avg_salary
FROM dept d
LEFT JOIN emp e ON d.dept_id = e.dept_id
GROUP BY d.dept_name
WITH READ ONLY;
-- DML を試みると ORA-42399 エラー UPDATE v_dept_stats SET dept_name = '開発部' WHERE dept_name = '開発'; -- ORA-42399: 読取り専用ビューに対してDML操作を実行できません
・
WITH CHECK OPTION:DML は許可するが、ビューの WHERE 条件を外れる DML だけ拒否したい・
WITH READ ONLY:DML を一切許可したくない(参照専用ビュー)アクセス制御のためのビュー設計
ビューを使うと、特定のユーザーに対して特定の列・特定の行だけを見せるきめ細かいアクセス制御が実現できます。テーブルへの直接アクセスは禁止し、ビュー越しのアクセスのみ許可します。
-- 給与(salary)を除いた社員情報ビューを作成 CREATE VIEW v_emp_public AS SELECT emp_id, emp_name, dept_id, hire_date FROM emp; -- salary 列はビューに含まれないため、このビューを通じては見えない -- テーブルへの直接アクセスは禁止し、ビューへのアクセスのみ許可する REVOKE SELECT ON emp FROM general_user; GRANT SELECT ON v_emp_public TO general_user;
-- ログインユーザーが所属する部署の社員のみ表示するビュー
-- SYS_CONTEXT で現在のDBユーザー名を取得
CREATE VIEW v_emp_my_dept AS
SELECT e.emp_id, e.emp_name, e.salary
FROM emp e
WHERE e.dept_id = (
SELECT dept_id FROM emp
WHERE emp_name = SYS_CONTEXT('USERENV', 'SESSION_USER')
);
-- SYS_CONTEXT('USERENV','SESSION_USER') は現在のログインユーザー名を返す
-- USER キーワードより確実で、パッケージ実行時のコンテキスト切り替えにも対応できる
① ベーステーブルへの
SELECT 権限を REVOKE で剥奪する② アクセスさせたいビューに
GRANT SELECT する③ 更新を許可する場合は
GRANT INSERT / UPDATE / DELETE ON ビュー名 を追加する権限の管理方法は【Oracle】ユーザ権限を確認する方法完全ガイドを参照してください。
DROP VIEW:ビューの削除
DROP VIEW v_emp_it;
-- Oracle 23c 以降は IF EXISTS が使える
DROP VIEW IF EXISTS v_emp_it;
-- 23c 未満では例外ハンドリングで対処
BEGIN
EXECUTE IMMEDIATE 'DROP VIEW v_emp_it';
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -942 THEN RAISE; END IF; -- ORA-00942: 表またはビューが存在しません
END;
/
ビューは仮想テーブルのためデータを持ちません。
DROP VIEW してもベーステーブルのデータは一切削除されません。ただし、削除したビューを参照している別のビューやプロシージャが INVALID になります。削除前に
USER_DEPENDENCIES で依存オブジェクトを確認してください。ビューのパフォーマンスへの影響
ビューはクエリを簡略化できる一方、使い方を誤るとパフォーマンスを悪化させることがあります。
| パターン | 問題 | 対処法 |
|---|---|---|
| ビューにビューを重ねる(多段ビュー) | オプティマイザが最適化しにくく、フルスキャンが多発することがある | 3段以上の多段ビューは避ける。マテリアライズドビューへの切り替えを検討 |
| ビューに集計・DISTINCT を含む | ビューを呼ぶたびに集計処理が走る | 結果を頻繁に使う場合はマテリアライズドビューを検討 |
| ビューの SELECT * を使う | ビューが再コンパイルされたときに列定義が変わる可能性がある | 列名を明示的に指定する |
| WHERE 条件をビューの外から渡す | ビュー定義によっては WHERE がビューの内側に押し込まれず全件スキャンになる | 実行計画(EXPLAIN PLAN)で確認し、必要であればビュー定義に条件を取り込む |
通常のビューは参照のたびに SELECT が実行されます。集計や複雑な JOIN を含むビューを頻繁に参照する場合は、あらかじめ結果をテーブルに保存するマテリアライズドビューが有効です。マテリアライズドビューの詳細は【Oracle】マテリアライズドビューの作成・管理・リフレッシュ完全ガイドを参照してください。
実践的なビューの活用パターン
CREATE OR REPLACE VIEW v_order_detail_full AS
SELECT
od.order_id,
od.line_no,
p.product_name,
od.qty,
od.unit_price,
od.qty * od.unit_price AS subtotal,
ROUND(od.qty * od.unit_price * (1 + od.tax_rate)) AS total_with_tax,
c.customer_name,
o.order_date
FROM order_detail od
JOIN orders o ON od.order_id = o.order_id
JOIN product p ON od.product_id = p.product_id
JOIN customer c ON o.customer_id = c.customer_id
WITH READ ONLY;
-- 個人情報(生年月日・住所)を除いた社員ビュー CREATE OR REPLACE VIEW v_emp_safe AS SELECT emp_id, emp_name, dept_id, hire_date FROM emp WITH READ ONLY; GRANT SELECT ON v_emp_safe TO general_role;
-- 有効な商品だけを扱うビュー(無効化された商品への変更を禁止) CREATE OR REPLACE VIEW v_product_active AS SELECT product_id, product_name, price, stock FROM product WHERE is_active = 1 WITH CHECK OPTION CONSTRAINT ck_v_product_active;
よくある質問
ORA-01402: ビューのWITH CHECK OPTIONの条件に違反しました は、ビューに WITH CHECK OPTION が設定されているとき、ビューの WHERE 条件を満たさない値で INSERT / UPDATE しようとしたときのエラーです。① INSERT する値がビューの WHERE 条件(例:
status = 'ACTIVE')を満たしているか確認する。② 条件を外れる DML を許可したい場合は
WITH CHECK OPTION を外す。ORA-01779: キー保存されていないテーブルにマップする列の変更は許可されていません は、JOIN を含むビューで、更新対象の列が「キー保存テーブル」でないテーブルに属している場合のエラーです。JOIN ビューへの DML は、プライマリキーを持つ「キー保存テーブル」の列のみ可能です。
一般的な対処法は以下の2つです。
①参照専用でよければ
WITH READ ONLY を付ける。② DML を受け付けたい場合は INSTEAD OF トリガーを使います。INSTEAD OF トリガーはビューに対する INSERT / UPDATE / DELETE を「代わりに(INSTEAD OF)実行する処理」を定義できます:
CREATE TRIGGER trg_v_emp_dept_upd INSTEAD OF UPDATE ON v_emp_with_dept FOR EACH ROW BEGIN UPDATE emp SET dept_id = :NEW.dept_id WHERE emp_id = :NEW.emp_id; END;WITH READ ONLY をビュー定義の末尾に追加してください。これにより INSERT / UPDATE / DELETE がすべて禁止され、ORA-42399 エラーが返るようになります。DROP VIEW してもベーステーブルのデータは消えません。ただし、削除したビューに依存している他のビュー・プロシージャ・トリガーが INVALID になる点に注意してください。SELECT text FROM USER_VIEWS WHERE VIEW_NAME = 'ビュー名'; で確認できます。USER_VIEWS / ALL_VIEWS / DBA_VIEWS の使い方や INVALID ビューへの対処は【Oracle】ビューの情報を取得する方法で詳しく解説しています。CREATE FORCE VIEW を使います。参照先のテーブルやビューが存在しない状態でも作成できます。ただし参照先が存在するまで INVALID 状態になるため、参照先を作成した後にビューが VALID になっていることを確認してください。(SELECT status FROM USER_OBJECTS WHERE OBJECT_NAME = 'ビュー名';)EXPLAIN PLAN)でビューの中でフルスキャンが発生していないか確認する。② ビューを呼ぶ側の WHERE 条件がビュー定義内に正しく渡っているか確認する(述語プッシュダウン)。
③ 集計を含むビューを頻繁に参照する場合はマテリアライズドビューへの切り替えを検討する。
④ 多段ビュー(ビューからビューを参照)は避けることを推奨します。
まとめ
Oracle のビュー操作のポイントをまとめます。
| やりたいこと | 方法 |
|---|---|
| ビューを作成する | CREATE VIEW … AS SELECT … |
| ビューの定義を変更する(GRANT を維持) | CREATE OR REPLACE VIEW |
| ビューの条件を外れる DML を禁止する | WITH CHECK OPTION |
| DML を一切禁止する(参照専用) | WITH READ ONLY |
| テーブルが存在しない状態でビューを作る | CREATE FORCE VIEW |
| 特定の列・行だけ見せるアクセス制御 | ビューに必要な列・条件のみ定義 → テーブルの権限を剥奪してビューのみ付与 |
| ビューの定義を確認する | SELECT text FROM USER_VIEWS(詳細は 2135 の記事を参照) |
| ビューを削除する | DROP VIEW(ベーステーブルのデータは消えない) |
ビューの定義確認・INVALID ビューの対処・USER_VIEWS / DBA_VIEWS の活用については【Oracle】ビューの情報を取得する方法を参照してください。
