postgreSQL その8 課題リストは2018/4/30時点でのオイラの所見
[P.248]
■トリガ
【概要】
トリガとは、特定のテーブルに対して挿入/更新/削除が行われた場合に、既定のユーザ定義関数(プロシージャ)を発動させる機能。
トリガの発動タイミングは下記のような場合に指定できる。
・行ごと、SQLごとを指定する
トリガの発動を、処理対象の行ごとに行うか、あるいはSQLごとに行うかを指定できる
・UPDATE/INSERT/DELETE/TRUNCATEの種別を指定する
・トリガの発動を、更新の種類で指定できる
INSERTとUPDATEのときだけトリガを発動させる、などの指定が可能
・更新対象の列を指定する
特定の列の更新をトリガの発動の対象にする、といった指定が可能
・実際に更新されたかどうかを指定する
実際に値が更新されたらトリガを発動させる、といった指定が可能。
特定の列を対象にすることも可能
・更新の前後を指定する
更新処理の前後のどちらでトリガを発動させるかを指定できる。
更新処理の前でトリガを発動させる場合、更新結果を参照することはできない。
【トリガの作成】
[書式]
create trigger トリガ名 { before | after } { update | insert | delete | truncate } on テーブル名
[for] [each] { row | statement } ] execute procedure 関数名;
更新前後のどちらで発動するかを[before]もしくは[after]で指定する。
更新種別としては、updateなどを指定する。 更新種別は、orでつなげて複数指定することも可能。 updateを指定した場合に限り、「update of 列名」という書式で、特定の列が更新されたときにトリガを発動させることができる。 特定の列が更新されたかどうかは、newとoldというキーワードを使って判別する。 oldは更新前、newは更新後の値をそれぞれ参照するときに使う。
updateのトリガは双方を使えるが、insertはnewのみ、deleteはold飲み使える。行ごとの発動かSQL文ごとの発動かは、「row」、「statement」で指定できる。 トリガの実際の処理となる関数は、最後の「関数名」に指定する。
次の例では、トリガで発動させる関数をtrg_f()として、いくつかの種類のトリガを指定する。
[例]
create table tb1 (c1 int, c2 text, c3 text);
・tb1へのupdateとinsert処理の実行後に、行ごとにトリガを起動
create trigger trg_1 after update or insert on tb1 for each row execute procedure trg_f();
・tb1のc1列の値を実際に変更したupdateの実行後に、行ごとにトリガを起動
create trigger trg_2 after update of c1 on tb1 for each row when (old.c1 is distinct from new.c1) execute procedure trg_f();
【トリガの削除】
[書式]
drop trigger トリガ名 on テーブル名;
■ルール
【概要】
ルールは、ある条件のSQLが発行された場合、それを別のSQLで置き換えたり、別の処理を付け加えたりしたい場合に使用する。 たとえば、あるテーブルへのINSERT処理を別のテーブルへのINSERTに置き換えたり、あるテーブルへのSELECTを別のテーブルへのSELECTに置き換えたりすることができる。
PostgreSQLのビューは、このルールという仕組みを利用する。
ビューが使用される際は、ルールによりビューを定義したSQLへ置き換えられる。
【ルールの作成】
[書式]
create rule ルール名 as on イベント名 to テーブル名 do [ instead | also ] SQL文;
「イベント名」には、「テーブル名」に対して実施されるINSERT/SELECT/UPDATE/DELETEのいずれかを指定する。 本来のSQLの処理を置き換える処理を定義したい場合は
「DO INSTEAD」を、追加で実施したい処理を定義したい場合は「DO ALSO」を指定する。
また、「イベント名」にINSERTかUPDATEを指定した場合には、「SQL文」でNEWというキーワードを使用できる。 これは、本来実施されるSQLでINSERTあるいはUPDATEされる新規の値を参照したい場合に使用する。
次の例では、tb1テーブルへのINSERT時に、tbl_logテーブルへ同じデータとINSERT時のタイムスタンプを挿入するルールを作成する。
[例]
create table tb12 (c1 int);
create table tb12_log (c1 int,c2 timestamp);
・ルールを作成。tb12へinsertされた値を、new c1でtb12_logにも入れる
create rule tb12_ins as on insert to tb12 do also insert into tb12_log values (new.c1, now());
・tb12にデータを挿入
insert into tb12 values(100);
・tb12へ挿入した値と挿入時のタイムスタンプがtb12_logに格納されているか確認
select * from tb12_log;
c1 | c2
-----+----------------------------
100 | 2018-05-22 19:01:30.036252
(1 row)
【ルールの削除】
[書式]
drop rule ルール名 on テーブル名;
■スキーマ
スキーマは名前空間とも呼ばれ、テーブルや関数などのオブジェクトが含まれる。
データベースの中に複数のスキーマが存在しており、各スキーマの中にテーブルやインデックスが含まれているイメージとなる。 PostgreSQLでは、デフォルトでpublicスキーマが作成されており、通常はこのpublicスキーマの中にテーブルやインデックスが作成される。
【スキーマの作成】
[書式]
create schema スキーマ名 [authorization データベースユーザ名];
create schema [authorization データベースユーザ名];
AUTHORIZATIONを使用すると、スキーマの所有者を指定できる。
省略した場合は、発行したデータベースユーザ名を所有者としてスキーマが作成される。
次の例では、schemaという名前のスキーマを作成している。
[例]
select user;
user
----------
postgres
(1 row)
create schema test authorization punisuke;
create schema authorization punisuke;
create schema unko_schema;
\dn
List of schemas
Name | Owner
-------------+----------
public | postgres
punisuke | punisuke
test | punisuke
testsch | postgres
testschema | postgres
unko_schema | postgres
(6 rows)
【スキーマへのテーブルの作成とスキーマ検索パス】
特定のスキーマにテーブルを作成するには、次のようにテーブル名をスキーマ名で修飾する。
インデックスはスキーマ名で修飾することができず、インデックスを付与するテーブルと同じスキーマに自動的に作成される。
[例]
testdb=# \c testdb punisuke
Password for user punisuke:
You are now connected to database "testdb" as user "punisuke".
testdb=> select user;
user
----------
punisuke
(1 row)
testdb=> \dn
List of schemas
Name | Owner
-------------+----------
public | postgres
punisuke | punisuke
test | punisuke
testsch | postgres
testschema | postgres
unko_schema | postgres
(6 rows)
testdb=> set search_path to public;
SET
testdb=> select current_schema;
current_schema
----------------
public
(1 row)
testdb=> set search_path to testschema;
SET
testdb=> select current_schema;
current_schema
----------------
testschema
(1 row)
→自分が所有者でないスキーマもSETできる
testdb=> create table punisuke_tb1 (c1 int, c2 text);
CREATE TABLE
testdb=> select * from pg_tables where schemaname = 'testschema' and tableowner = 'punisuke';
schemaname | tablename | tableowner | tablespace | hasindexes | hasrules | hastriggers | rowsecurity
------------+--------------+------------+------------+------------+----------+-------------+-------------
testschema | punisuke_tb1 | punisuke | | f | f | f | f
(1 row)
testdb=> \dt testschema.punisuke_tb1
List of relations
Schema | Name | Type | Owner
------------+--------------+-------+----------
testschema | punisuke_tb1 | table | punisuke
(1 row)
→自分が所有者でないスキーマの中にテーブルを作成できる
testdb=> insert into testschema.punisuke_tb1 values (1,'あいう');
INSERT 0 1
→カレントスキーマでスキーマ名を修飾せずにテーブルを作成したらカレントスキーマの中にテーブルが作成される
→自分が所有者でないスキーマの中にテーブルに更新系SQLを実行できる
testdb=> select * from testschema.punisuke_tb1;
c1 | c2
----+--------
1 | あいう
(1 row)
→自分が所有者でないスキーマの中にテーブルにSQLを実行できる
testdb=> \d testschema.punisuke_tb1
Table "testschema.punisuke_tb1"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+---------
c1 | integer | | |
c2 | text | | |
Indexes:
"punisuke_tb1_idx" btree (c1)
→自分が所有者でないスキーマの中のテーブルにインデックスを作成できる
異なるスキーマ間で同じ名前のテーブルや関数を作成できる。
このように、複数のスキーマに渡って存在する同名のテーブルへのアクセスする際には、
①テーブルや関数をスキーマ名で修飾する方法
②スキーマ検索パスを設定する方法
の2つの方法がある。
検索パスを使うと、どのスキーマのオブジェクトに優先的にアクセスするかを指定できる。
スキーマ検索パスの指定方法には、
(1) postgresql.confのsearch_pathパラメータに指定する方法
(2) SETを使って、search_pathパラメータを動的に変更する方法
がある。
search_pathのデフォルト値は「$user,public」となっており、現在のデータベースユーザ名と同じ名前のスキーマにあるオブジェクトを優先的に使用するようになっている。($userは現在のデータベースオブジェクトを表すエイリアス)
setを使ったsearch_pathパラメータの変更方法は下記の通り
[書式]
set search_path to スキーマ名;
スキーマ名はカンマ区切りで複数を指定することができる。$userのみダブルクォートで囲む。
標準的に使われる関数はすべてpublicスキーマにあるため、setで変更する場合はpublicを設定し忘れないように注意する。
次の例では、異なるスキーマに作成した2つのテーブルに対してSQLを発行している。
[例]
testdb=> create table punisuke.punisuke_tb1 (c1 int, c2 text);
CREATE TABLE
testdb=> insert into punisuke.punisuke_tb1 values (1,'かきく');
INSERT 0 1
testdb=> select current_schema;
current_schema
----------------
testschema
(1 row)
testdb=> select * from punisuke_tb1;
c1 | c2
----+--------
1 | あいう
(1 row)
testdb=> select * from punisuke.punisuke_tb1;
c1 | c2
----+--------
1 | かきく
(1 row)
testdb=> set search_path to punisuke;
SET
testdb=> select current_schema;
current_schema
----------------
punisuke
(1 row)
testdb=> select * from punisuke_tb1;
c1 | c2
----+--------
1 | かきく
(1 row)