PostgreSQL Advent Calendar 2012(全部俺)のDay 20です。
先日、ネット上でも少し話題になっていましたが、開発中のPostgreSQLの次バージョン(9.3)に「更新可能ビュー」をサポートするコードがコミットされました。
pgsql: Support automatically-updatable views.
http://archives.postgresql.org/pgsql-committers/2012-12/msg00154.php
今回はこの更新可能ビューについて、その制約なども含めてどのようなものなのかを見てみたいと思います。
なお、今回は開発中のバージョンで試しますので、試してみたい方はPostgreSQLのGitレポジトリからソースコードを取得して自身でビルドしてください。
Working with Git - PostgreSQL wiki
http://wiki.postgresql.org/wiki/Working_with_Git
「更新可能ビュー」とはどういった機能でしょうか。
RDBMSにおけるビューのしくみを理解している方は、それを思い浮かべていただければ分かるかと思いますが、ビューの定義が「十分に簡単」である場合、ビューに対するINSERTやUPDATE、DELETE処理は、元テーブルに対する更新処理に書き換えることが可能になるはずです。
この「ビューに対する更新処理を、元テーブルに対する更新処理へ自動的に書き換える」という機能が、「automatically updatable、自動的に更新できる」ビューと言われる機能になります。
更新可能ビューが実装されたとは言え、「十分に簡単」という条件が付いているように、すべてのビューに対して更新が可能なわけではなく、その制約・前提条件があります。
なぜなら、「ビューへの更新処理」というのは、ビューの定義から参照元テーブルのレコード構造、つまり通常のビューとは「逆方向」に展開・変換する作業に他なりませんので、論理的に元テーブルのレコードを再構成できないと実行ができないのです。
更新可能ビューとしての前提条件は、開発版のオフィシャルマニュアルに記載がありましたので、以下に簡単に日本語に翻訳してきます。
詳細については、オフィシャルマニュアルの記載を参照してください。
PostgreSQL: Documentation: devel: CREATE VIEW
http://www.postgresql.org/docs/devel/static/sql-createview.html
では最初に、もっとも基本的なビューを定義してレコードを更新できるか試してみます。
次に、今度はビューに対して2件目のレコードをINSERTしてみます。
次に、ビューのselectリストに含まれないカラムがあった場合に、どのように扱われるのかを見てみます。
「ビューのselectリストに含まれない」ということは、ビューへの更新時に値が存在していないため元テーブルに値を渡せない、という状況になるはずです。
以下の例では、uidとunameという元テーブルのカラムのうち、uidカラムだけを使ってビューを定義し、そのビューに対して更新(INSERT)を行おうとしています。
ここで、元テーブルのunameカラムにデフォルト値を設定して、再度ビューv2に対して更新してみます。
ここまで見ると分かりますが、ビューのselectリストに含まれないカラムは、
今回は、次のリリースで提供されることになった「更新可能ビュー」の機能について、どのような機能なのか、そしてどのように動くのかを、簡単にではありますが見てきました。
前述した通り、すべてのビューが更新可能になるわけではありませんし、実際に使うビューはここで示したより複雑ですので、利用にはいろいろ制約があるかと思います。
しかし、場合によっては開発時に便利に使えることもあるかと思いますので、機会があればぜひ試してみていただければと思います。
では、また。
先日、ネット上でも少し話題になっていましたが、開発中のPostgreSQLの次バージョン(9.3)に「更新可能ビュー」をサポートするコードがコミットされました。
pgsql: Support automatically-updatable views.
http://archives.postgresql.org/pgsql-committers/2012-12/msg00154.php
今回はこの更新可能ビューについて、その制約なども含めてどのようなものなのかを見てみたいと思います。
なお、今回は開発中のバージョンで試しますので、試してみたい方はPostgreSQLのGitレポジトリからソースコードを取得して自身でビルドしてください。
Working with Git - PostgreSQL wiki
http://wiki.postgresql.org/wiki/Working_with_Git
■「更新可能ビュー」とは
「更新可能ビュー」とはどういった機能でしょうか。
RDBMSにおけるビューのしくみを理解している方は、それを思い浮かべていただければ分かるかと思いますが、ビューの定義が「十分に簡単」である場合、ビューに対するINSERTやUPDATE、DELETE処理は、元テーブルに対する更新処理に書き換えることが可能になるはずです。
この「ビューに対する更新処理を、元テーブルに対する更新処理へ自動的に書き換える」という機能が、「automatically updatable、自動的に更新できる」ビューと言われる機能になります。
■更新可能ビューの制約・前提条件
更新可能ビューが実装されたとは言え、「十分に簡単」という条件が付いているように、すべてのビューに対して更新が可能なわけではなく、その制約・前提条件があります。
なぜなら、「ビューへの更新処理」というのは、ビューの定義から参照元テーブルのレコード構造、つまり通常のビューとは「逆方向」に展開・変換する作業に他なりませんので、論理的に元テーブルのレコードを再構成できないと実行ができないのです。
更新可能ビューとしての前提条件は、開発版のオフィシャルマニュアルに記載がありましたので、以下に簡単に日本語に翻訳してきます。
- 単一のテーブル、または更新可能ビューのみをFROM句に持つこと。(結合をしていないこと)
- ビューの定義がWITH、DISTINCT、GROUP BY、HAVING、LIMIT、OFFSETを持たないこと。
- ビューの定義がUNION、INTERSECT、EXCEPTを持たないこと。
- ビューの定義のselectリストに出てくるカラムが、元テーブルのカラムをそのまま参照していること。expressionやリテラル、関数であってはならない。システムカラムであってもならない。
- 元テーブルのカラムが二回以上ビューの定義に出てこないこと。
- ビューがsecurity_barrier属性を持たないこと。
詳細については、オフィシャルマニュアルの記載を参照してください。
PostgreSQL: Documentation: devel: CREATE VIEW
http://www.postgresql.org/docs/devel/static/sql-createview.html
■更新可能ビューを使ってみる
では最初に、もっとも基本的なビューを定義してレコードを更新できるか試してみます。
まず、元テーブルに対して1件レコードをINSERTします。
[snaga@devsv02 updatableview]$ /tmp/pgsql/bin/psql testdb
psql (9.3devel)
Type "help" for help.
testdb=# CREATE TABLE t1 ( uid INTEGER PRIMARY KEY, uname TEXT NOT NULL );
CREATE TABLE
testdb=# CREATE VIEW v1 AS SELECT * FROM t1;
CREATE VIEW
testdb=# select * FROM t1;
uid | uname
-----+-------
(0 rows)
testdb=#
当然、元テーブルからもビューからもレコードが見えます。
testdb=# insert into t1 values ( 101, 'Park Gyu-Ri' );
INSERT 0 1
testdb=# select * from t1;
uid | uname
-----+-------------
101 | Park Gyu-Ri
(1 row)
testdb=# select * from v1;
uid | uname
-----+-------------
101 | Park Gyu-Ri
(1 row)
testdb=#
次に、今度はビューに対して2件目のレコードをINSERTしてみます。
ビューに対するレコードのINSERTも成功し、元テーブルに対してレコードがINSERTされていることが分かります。この時の実行プランを見てみると、INSERT文にビューv1を指定しているにも関わらず、内部的にはテーブルt1に更新しようとしていることが分かります。
testdb=# insert into v1 values ( 102, 'Han Seung-Yeon' );
INSERT 0 1
testdb=# select * from t1;
uid | uname
-----+----------------
101 | Park Gyu-Ri
102 | Han Seung-Yeon
(2 rows)
testdb=# select * from v1;
uid | uname
-----+----------------
101 | Park Gyu-Ri
102 | Han Seung-Yeon
(2 rows)
testdb=#
ビューv1に対するUPDATEについても、同様にテーブルt1への更新として内部的に書き換えられています。
testdb=# explain insert into v1 values ( 103, 'Nicole' );
QUERY PLAN
------------------------------------------------
Insert on t1 (cost=0.00..0.01 rows=1 width=0)
-> Result (cost=0.00..0.01 rows=1 width=0)
(2 rows)
testdb=#
testdb=# explain update v1 set uname = 'Nicole' where uid = 102;
QUERY PLAN
-------------------------------------------------------------------------
Update on t1 (cost=0.00..8.27 rows=1 width=10)
-> Index Scan using t1_pkey on t1 (cost=0.00..8.27 rows=1 width=10)
Index Cond: (uid = 102)
(3 rows)
testdb=#
■ビューのselectリストに含まれないカラム
次に、ビューのselectリストに含まれないカラムがあった場合に、どのように扱われるのかを見てみます。
「ビューのselectリストに含まれない」ということは、ビューへの更新時に値が存在していないため元テーブルに値を渡せない、という状況になるはずです。
以下の例では、uidとunameという元テーブルのカラムのうち、uidカラムだけを使ってビューを定義し、そのビューに対して更新(INSERT)を行おうとしています。
このINSERT文はエラーになりましたが、エラーになっている理由を見ると、「元テーブルのunameカラムにnot-null制約が付いており、それに違反しているのでエラー」と言われていることが分かります。
testdb=# create view v2 as select uid from t1;
CREATE VIEW
testdb=# insert into v2 values ( 103 );
ERROR: null value in column "uname" violates not-null constraint
DETAIL: Failing row contains (103, null).
STATEMENT: insert into v2 values ( 103 );
testdb=#
ここで、元テーブルのunameカラムにデフォルト値を設定して、再度ビューv2に対して更新してみます。
今度はエラーにはならず、unameカラムにはデフォルト値の "(noname)" という値が設定されました。
testdb=# ALTER TABLE t1 ALTER COLUMN uname SET DEFAULT '(noname)';
ALTER TABLE
testdb=# insert into v2 values ( 103 );
INSERT 0 1
testdb=# select * from t1;
uid | uname
-----+----------------
101 | Park Gyu-Ri
102 | Han Seung-Yeon
103 | (noname)
(3 rows)
testdb=#
ここまで見ると分かりますが、ビューのselectリストに含まれないカラムは、
- 元テーブルのカラムにデフォルト値があればデフォルト値に設定。
- デフォルト値が無ければnullに設定。
- その上で、元テーブルの制約でチェックされる。
■まとめ
今回は、次のリリースで提供されることになった「更新可能ビュー」の機能について、どのような機能なのか、そしてどのように動くのかを、簡単にではありますが見てきました。
前述した通り、すべてのビューが更新可能になるわけではありませんし、実際に使うビューはここで示したより複雑ですので、利用にはいろいろ制約があるかと思います。
しかし、場合によっては開発時に便利に使えることもあるかと思いますので、機会があればぜひ試してみていただければと思います。
では、また。