postgreSQL その27 SQL テーブルの定義 その1のつづき

postgreSQL その8 課題リストは2018/4/30時点でのオイラの所見

 

[P.239]

■外部キー制約

外部キー制約は参照整合性制約とも呼ばれ、あるテーブルの列が別のテーブルの列を参照することで実現している。 外部キーを使用することで、例えば、テーブルAのid列がテーブルBのid列を参照し、テーブルBのid列に無い値がテーブルAのid列に入るのを防ぐことができる。

 

[書式]

create table テーブル名 (列名 データ型 references 参照先テーブル名(列名));

create table テーブル名 (列名 データ型, foreign key(列名) references 参照先テーブル名(列名));

alter table テーブル名 add constraint 外部キー名 foreign key (列名) references 参照先テーブル名(列名);

 

削除するときは

 

alter table テーブル名 drop constraint 外部キー名:

 

「外部キー名」を指定しないCREATE TABLE の場合は「テーブル名_列名_fkey」という名前で外部キーが作成される。参照先のテーブルの列には、主キー制約かユニーク制約が設定されている必要がある。

 

[例]

city3テーブルの「name」列をユニーク制約、tb8の「city」列からcity3テーブルの「name」列を参照させる外部キー制約を設定する。

testdb=# select * from city3;
 id |  name  | population | prefecture |  地方  | 県庁所在地 | 政令指定都市 |       comment
----+--------+------------+------------+--------+------------+--------------+----------------------
  1 | 前橋   |         30 | 群馬       | 関東   | t          | f            | ぐんまー
  2 | 千葉   |        100 | 千葉       | 関東   | t          | t            | ちばらぎ
  3 | 郡山   |         40 | 福島       | 東北   | f          | f            |
  4 | 横浜   |        350 | 神奈川     | 関東   | t          | t            | オサレ
  5 | 福生   |         12 | 東京       | 関東   | f          | f            | 米軍基地
  6 | 那覇   |         30 | 沖縄       | 九州   | t          | f            | しまんちゅ
  7 | 旭川   |         30 | 北海道     | 北海道 | f          | f            | かなり寒い
  8 | 習志野 |         20 | 千葉       | 関東   | f          | f            | 空挺部隊
  9 | 藤沢   |         40 | 神奈川     | 関東   | f          | f            | 湘南ナンバーぶいぶい
 10 | 相模原 |         40 | 神奈川     | 関東   | f          | f            |
 11 | 町田   |         40 | 東京       | 関東   | f          | f            | 神奈川県じゃないんご
 12 | 和歌山 |         25 | 和歌山     | 近畿   | t          | f            |
(12 rows)

testdb=# select * from tb8;
    first     |     last     | city
--------------+--------------+------
 鈴木         | 一郎         | 前橋
 鈴木         | 次郎         | 前橋
 田中         | 次郎         | 横浜
 田中         | 一郎         | 前橋
(4 rows)

testdb=# \d city3
                       Table "testschema.city3"
    Column    |         Type          | Collation | Nullable | Default
--------------+-----------------------+-----------+----------+---------
 id           | integer               |           |          |
 name         | character varying(30) |           |          |
 population   | integer               |           |          |
 prefecture   | character varying(30) |           |          |
 地方         | character varying(10) |           |          |
 県庁所在地   | boolean               |           |          |
 政令指定都市 | boolean               |           |          |
 comment      | character varying(30) |           |          |

testdb=# \d tb8
                     Table "testschema.tb8"
 Column |         Type          | Collation | Nullable | Default
--------+-----------------------+-----------+----------+---------
 first  | character(10)         |           | not null |
 last   | character(10)         |           | not null |
 city   | character varying(10) |           |          |
Indexes:
    "tb8_pkey" PRIMARY KEY, btree (first, last)

testdb=# alter table city3 add unique (name);
ALTER TABLE
testdb=# \d city3
                       Table "testschema.city3"
    Column    |         Type          | Collation | Nullable | Default
--------------+-----------------------+-----------+----------+---------
 id           | integer               |           |          |
 name         | character varying(30) |           |          |
 population   | integer               |           |          |
 prefecture   | character varying(30) |           |          |
 地方         | character varying(10) |           |          |
 県庁所在地   | boolean               |           |          |
 政令指定都市 | boolean               |           |          |
 comment      | character varying(30) |           |          |
Indexes:
    "city3_name_key" UNIQUE CONSTRAINT, btree (name)   ←「name」列にユニーク制約が作成された

testdb=# alter table tb8 add constraint chinko foreign key (city) references city3(name);
ALTER TABLE
testdb=# \d tb8
                     Table "testschema.tb8"
 Column |         Type          | Collation | Nullable | Default
--------+-----------------------+-----------+----------+---------
 first  | character(10)         |           | not null |
 last   | character(10)         |           | not null |
 city   | character varying(10) |           |          |
Indexes:
    "tb8_pkey" PRIMARY KEY, btree (first, last)
Foreign-key constraints:
    "chinko" FOREIGN KEY (city) REFERENCES city3(name)   ←「city」列に外部キー制約が作成された

 

tb8に外部キー制約で許されている挿入をするんご

testdb=# insert into tb8 values ('蘇我','入鹿','横浜');
INSERT 0 1
testdb=# select * from tb8;
    first     |     last     | city
--------------+--------------+------
 鈴木         | 一郎         | 前橋
 鈴木         | 次郎         | 前橋
 田中         | 次郎         | 横浜
 田中         | 一郎         | 前橋
 蘇我         | 入鹿         | 横浜
(5 rows)

 

tb8に外部キー制約で許されてない挿入をするんご

testdb=# insert into tb8 values ('坂上','田村麻呂','豊原');
ERROR:  insert or update on table "tb8" violates foreign key constraint "chinko"
DETAIL:  Key (city)=(豊原) is not present in table "city3".

 

外部キーによって参照されているテーブル(たいていマスターテーブル)の列を削除/更新すると、制約に違反することになる。このような処理を行う場合、エラーにさせる(参照されているデータは変更させない)方法と、参照しているデータも削除/更新してしまう方法の2つを選択できる。 defaultではエラーとなる。 もし、参照先のデータ削除/更新と連動して、参照元のデータも削除/更新する場合は下記の書式を利用する

 

[書式]

create table テーブル名(列名 データ型 references 参照先テーブル名 (列名) [on delete cascade] [on update cascade] );

 

alter table テーブル名 add constraint 外部キー名 foreign key (列名) references 参照先テーブル名(列名) [on delete cascade] [on update cascade];

 

ON DELETE CASCADE を付与することにより、参照先のデータの削除と連動して参照元のデータも削除される。 ON UPDATE CASCADE は更新と連動する。

 

[例]

・デフォルトの場合、city3から横浜を削除しようとすると参照先のtb8で横浜を参照しているからエラーになるか?

testdb=# select * from tb8;
    first     |     last     | city
--------------+--------------+------
 鈴木         | 一郎         | 前橋
 鈴木         | 次郎         | 前橋
 田中         | 次郎         | 横浜
 田中         | 一郎         | 前橋
 蘇我         | 入鹿         | 横浜
(5 rows)

testdb=# delete from city3 where name='横浜';
ERROR:  update or delete on table "city3" violates foreign key constraint "chinko" on table "tb8"
DETAIL:  Key (name)=(横浜) is still referenced from table "tb8".

→エラーになって削除できなかった

testdb=# select * from city3 where name='横浜';
 id | name | population | prefecture | 地方 | 県庁所在地 | 政令指定都市 | comment
----+------+------------+------------+------+------------+--------------+---------
  4 | 横浜 |        350 | 神奈川     | 関東 | t          | t            | オサレ
(1 row)

 

・「ON DELETE CASCADE」と「ON UPDATE CASCADE」を付与

testdb=# alter table tb8 drop constraint chinko;
ALTER TABLE
testdb=# alter table tb8 add constraint chinko foreign key (city) references city3(name) on delete cascade on update cascade;
ALTER TABLE
testdb=# \d tb8
                     Table "testschema.tb8"
 Column |         Type          | Collation | Nullable | Default
--------+-----------------------+-----------+----------+---------
 first  | character(10)         |           | not null |
 last   | character(10)         |           | not null |
 city   | character varying(10) |           |          |
Indexes:
    "tb8_pkey" PRIMARY KEY, btree (first, last)
Foreign-key constraints:
    "chinko" FOREIGN KEY (city) REFERENCES city3(name) ON UPDATE CASCADE ON DELETE CASCADE

 

city3の「横浜」を「横濱」に変更する

testdb=# update city3 set name = '横濱' where name = '横浜';
UPDATE 1
testdb=# select * from city3;
 id |  name  | population | prefecture |  地方  | 県庁所在地 | 政令指定都市 |       comment
----+--------+------------+------------+--------+------------+--------------+----------------------
  1 | 前橋   |         30 | 群馬       | 関東   | t          | f            | ぐんまー
  2 | 千葉   |        100 | 千葉       | 関東   | t          | t            | ちばらぎ
  3 | 郡山   |         40 | 福島       | 東北   | f          | f            |
  5 | 福生   |         12 | 東京       | 関東   | f          | f            | 米軍基地
  6 | 那覇   |         30 | 沖縄       | 九州   | t          | f            | しまんちゅ
  7 | 旭川   |         30 | 北海道     | 北海道 | f          | f            | かなり寒い
  8 | 習志野 |         20 | 千葉       | 関東   | f          | f            | 空挺部隊
  9 | 藤沢   |         40 | 神奈川     | 関東   | f          | f            | 湘南ナンバーぶいぶい
 10 | 相模原 |         40 | 神奈川     | 関東   | f          | f            |
 11 | 町田   |         40 | 東京       | 関東   | f          | f            | 神奈川県じゃないんご
 12 | 和歌山 |         25 | 和歌山     | 近畿   | t          | f            |
  4 | 横濱   |        350 | 神奈川     | 関東   | t          | t            | オサレ
(12 rows)

testdb=# select * from tb8;
    first     |     last     | city
--------------+--------------+------
 鈴木         | 一郎         | 前橋
 鈴木         | 次郎         | 前橋
 田中         | 一郎         | 前橋
 田中         | 次郎         | 横濱              ←変更された!
 蘇我         | 入鹿         | 横濱              ←変更された!
(5 rows)

testdb=# delete from city3 where name = '前橋';
DELETE 1
testdb=# select * from tb8;
    first     |     last     | city
--------------+--------------+------
 田中         | 次郎         | 横濱
 蘇我         | 入鹿         | 横濱
(2 rows)

→前橋な人たちが消えた

 

■チェック制約

チェック制約は、ある列に対して特定の条件を満たす値のみ許可する制約。

 

[書式]

create table テーブル名 (列名 データ型 check (条件式));

alter table テーブル名 add constraint チェック制約名 check (条件式);

 

「条件式」には、特定の列に対する指揮や関数を指定する。「条件式」の結果が真になるものだけを許可することになる。

「条件式」はANDでつなげることで、複数の式を一度に指定することも可能。 なお、「チェック制約名」を指定しない create table の場合は、「テーブル名_列名_check」という名前でチェック制約名が自動で付与される。 

 

[例]

testdb=# alter table city3 add constraint check_puni check (population > 0);
ALTER TABLE
testdb=# \d city3
                       Table "testschema.city3"
    Column    |         Type          | Collation | Nullable | Default
--------------+-----------------------+-----------+----------+---------
 id           | integer               |           |          |
 name         | character varying(30) |           |          |
 population   | integer               |           |          |
 prefecture   | character varying(30) |           |          |
 地方         | character varying(10) |           |          |
 県庁所在地   | boolean               |           |          |
 政令指定都市 | boolean               |           |          |
 comment      | character varying(30) |           |          |
Indexes:
    "city3_name_key" UNIQUE CONSTRAINT, btree (name)
Check constraints:
    "check_puni" CHECK (population > 0)

Referenced by:
    TABLE "tb8" CONSTRAINT "chinko" FOREIGN KEY (city) REFERENCES city3(name) ON UPDATE CASCADE ON DELETE CASCADE

testdb=# insert into city3 values (13,'キチガイ島',0,'キチガイ県','きち',false,false,null);
ERROR:  new row for relation "city3" violates check constraint "check_puni"
DETAIL:  Failing row contains (13, キチガイ島, 0, キチガイ県, きち, f, f, null).

testdb=# insert into city3 values (13,'キチガイ島',1,'キチガイ県','きち',false,false,null);
INSERT 0 1
testdb=# select * from city3 where id = 13;
 id |    name    | population | prefecture | 地方 | 県庁所在地 | 政令指定都市 | comment
----+------------+------------+------------+------+------------+--------------+---------
 13 | キチガイ島 |          1 | キチガイ県 | きち | f          | f            |
(1 row)

 

 

■ドメイン制約

ドメイン制約は、チェック制約を伴った独特のデータ型にどんな制約を付与するのかを定義する。 作成したドメイン制約については、通常のデータ型と同じように扱うことができる。

 

[例]

int型を対象に0より大きいという制約を付与して使用する。

testdb=# create domain int_p as integer check (value > 0);
CREATE DOMAIN
testdb=# create table tb9 (c1 int_p);
CREATE TABLE
testdb=# \d tb9
             Table "testschema.tb9"
 Column | Type  | Collation | Nullable | Default
--------+-------+-----------+----------+---------
 c1     | int_p |           |          |

testdb=# insert into tb9 values (1);
INSERT 0 1
testdb=# insert into tb9 values (-1);
ERROR:  value for domain int_p violates check constraint "int_p_check"
testdb=# select * from tb9;
 c1
----
  1
(1 row)