postgresql----數據庫表約束----FOREIGN KEY


六、FOREIGN KEY ---- 外鍵約束

外鍵可以是單個字段,也可以是多個字段。所謂的外鍵約束就是引用字段必須在被引用字段中存在,除非引用字段部分為NULL或全部為NULL(由MATCH TYPE決定),否則INSERT或UPDATE時將返回失敗,且被引用字段必須有唯一約束或是主鍵。

外鍵約束語法相對較復雜一點,創建外鍵的語法如下:

ALTER TABLE tbl_foreign CONSTRAINT fk_constraint FOREIGN KEY(col1,col2) REFERENCES tbl_foreign_refd(refd_col1,refd_col2) MATCH [SIMPLE|FULL] ON DELETE [CASCADE|NO ACTION] ON UPDATE [CASCADE|NO ACTION];

其中:

tbl_foreign             : 引用表

fk_constraint           : 外鍵約束名稱

(col1,col2)             : 引用表中引用字段

tbl_foreign_refd        : 被引用表

(refd_col1,refd_col2)   : 被引用表中被引用字段,和(col1,col2)對應

MATCH [SIMPLE|FULL]     : 外鍵匹配模式,如果引用字段全部不是NULL,則強匹配,否則根據匹配模式進行弱匹配。

--SIMPLE,默認值,只要引用字段中任一字段為NULL,則不要求與被引用字段強匹配;

--FULL,只有引用字段全部為NULL,才不要求與被引用字段強匹配。

 

ON DELETE [CASCADE | NO ACTION] : 默認NO ACTION

--CASCADE,刪除被引用表數據級聯刪除引用表數據

--NO ACTION,刪除被引用表數據必須先刪除引用表數據,否則,如果引用表如果存在數據,直接刪除被引用表數據返回失敗。

 

ON UPDATE [CASCADE | NO ACTION] : 默認NO ACTION

--CASCADE,更新被引用表時級聯更新引用表數據

--NO ACTION,更新被引用表時必須先刪除引用表數據,否則,如果引用表存在數據,直接更新被引用表數據返回失敗。

 

1.創建測試表(引用表tbl_foreign和被引用表tbl_foreign_refd)

create table tbl_foreign_refd(
a int not null,
b int not null,
c varchar
);
alter table tbl_foreign_refd add constraint pk_tbl_foreign_refd_a_b primary key(a,b);

create table tbl_foreign(
a int,
b int,
c varchar
);
alter table tbl_foreign add constraint fk_tbl_foreign_a_b foreign key(a,b) references tbl_foreign_refd(a,b);

上表中完整外鍵其實如下,因為match,on delete,on update會自動使用默認值。

test=# alter table tbl_foreign add constraint fk_tbl_foreign_a_b foreign key(a,b) references tbl_foreign_refd(a,b) match simple on delete no action on update no action;

 

測試例1.match simple on delete no action on update no action

test=# insert into tbl_foreign_refd (a,b) values (1,1),(1,2),(1,3);
INSERT 0 3
test=# insert into tbl_foreign(a,b) values (1,1),(1,2);
INSERT 0 2

test=# insert into tbl_foreign(a,b) values (2,1);
ERROR:  insert or update on table "tbl_foreign" violates foreign key constraint "fk_tbl_foreign_a_b"
DETAIL:  Key (a, b)=(2, 1) is not present in table "tbl_foreign_refd".

test=# insert into tbl_foreign(a) values (2);
INSERT 0 1
test=# insert into tbl_foreign(a) values (1);
INSERT 0 1
test=# select * from tbl_foreign;
 a |  b   |  c   
---+------+------
 1 |    1 | NULL
 1 |    2 | NULL
 2 | NULL | NULL
 1 | NULL | NULL
(4 rows)
test=# delete from tbl_foreign_refd where a=1 and b=1;
ERROR:  update or delete on table "tbl_foreign_refd" violates foreign key constraint "fk_tbl_foreign_a_b" on table "tbl_foreign"
DETAIL:  Key (a, b)=(1, 1) is still referenced from table "tbl_foreign".


test=# update tbl_foreign_refd set a=3 where a=1 and b=1;
ERROR:  update or delete on table "tbl_foreign_refd" violates foreign key constraint "fk_tbl_foreign_a_b" on table "tbl_foreign"
DETAIL:  Key (a, b)=(1, 1) is still referenced from table "tbl_foreign".

 

測試例2.match full on delete cascade on update cascade

刪除外鍵約束,清空數據,重新增加外鍵

test=# alter table tbl_foreign drop constraint fk_tbl_foreign_a_b ;
ALTER TABLE
test=# delete from tbl_foreign;
DELETE 4
test=# alter table tbl_foreign add constraint fk_tbl_foreign_a_b foreign key(a,b) references tbl_foreign_refd(a,b) match full on delete cascade on update cascade;
ALTER TABLE

 

test=# insert into tbl_foreign(a,b) values (1,1),(1,2);
INSERT 0 2

test=# insert into tbl_foreign(a) values (2);
ERROR:  insert or update on table "tbl_foreign" violates foreign key constraint "fk_tbl_foreign_a_b"
DETAIL:  MATCH FULL does not allow mixing of null and nonnull key values.

test=# insert into tbl_foreign(c) values (2);
INSERT 0 1
test=# select * from tbl_foreign;
  a   |  b   |  c   
------+------+------
    1 |    1 | NULL
    1 |    2 | NULL
 NULL | NULL | 2
(3 rows)

test=# delete from tbl_foreign_refd where a=1 and b=1;
DELETE 1
test=# update tbl_foreign_refd set a=2 where a=1 and b=2;
UPDATE 1
test=# select * from tbl_foreign;
  a   |  b   |  c   
------+------+------
 NULL | NULL | 2
    2 |    2 | NULL
(2 rows)

test=# update tbl_foreign set a=3 where a=2;
ERROR:  insert or update on table "tbl_foreign" violates foreign key constraint "fk_tbl_foreign_a_b"
DETAIL:  Key (a, b)=(3, 2) is not present in table "tbl_foreign_refd".

 

2.刪除外鍵約束

alter table tbl_foreign drop constraint fk_tbl_foreign_a_b ;

 

3.增加外鍵約束

和唯一鍵,主鍵一樣,增加外鍵約束前首先要刪除臟數據,對外鍵來說臟數據針對不同的match type來說是不一樣的。

match simple : 引用字段全部是NOT NULL,且在被引用表中不存在的。

match full   : 引用字段部分是NULL和全部是NOT NULL且在被引用表中不存在的。

 

情況一:增加match simple類型外鍵

第一步:刪除外鍵,清空表,寫入測試數據

test=# alter table tbl_foreign drop constraint fk_tbl_foreign_a_b ;
ALTER TABLE
test=# delete from tbl_foreign;
DELETE 2
test=# insert into tbl_foreign(a,b) values (1,2),(2,2),(1,1);
INSERT 0 3
test=# insert into tbl_foreign(a) values (3),(4);
INSERT 0 2
test=# insert into tbl_foreign(c) values (5);
INSERT 0 1
test=# select * from tbl_foreign;
  a   |  b   |  c   
------+------+------
    1 |    2 | NULL
    2 |    2 | NULL
    1 |    1 | NULL
    3 | NULL | NULL
    4 | NULL | NULL
 NULL | NULL | 5
(6 rows)

test=# select * from tbl_foreign_refd ;
 a | b |  c   
---+---+------
 1 | 3 | NULL
 2 | 2 | NULL
(2 rows)

對於要增加的外鍵來說,(1,1,NULL),(1,2,NULL)是臟數據。

第二步:查詢臟數據

test=# select * from tbl_foreign where not exists (select null from tbl_foreign_refd where tbl_foreign_refd.a=tbl_foreign.a and tbl_foreign_refd.b=tbl_foreign.b) and a is not null and b is not null;
 a | b |  c   
---+---+------
 1 | 1 | NULL
 1 | 2 | NULL
(2 rows)

第三步:刪除臟數據

將上面SQL語句中的select替換成delete即可。

test=# delete from tbl_foreign where not exists (select null from tbl_foreign_refd where tbl_foreign_refd.a=tbl_foreign.a and tbl_foreign_refd.b=tbl_foreign.b) and a is not null and b is not null;
DELETE 2

第四步:增加外鍵

test=# alter table tbl_foreign add constraint fk_tbl_foreign_a_b foreign key(a,b) references tbl_foreign_refd(a,b) match simple;
ALTER TABLE

 

情況二:增加match full類型外鍵

第一步:刪除外鍵,清空表,寫入測試數據

test=# alter table tbl_foreign drop constraint fk_tbl_foreign_a_b ;
ALTER TABLE
test=# delete from tbl_foreign;
DELETE 4
test=# insert into tbl_foreign(a,b) values (1,2),(2,2),(1,1);
INSERT 0 3
test=# insert into tbl_foreign(a) values (3),(4);
INSERT 0 2
test=# insert into tbl_foreign(c) values (5);
INSERT 0 1
test=# select * from tbl_foreign;
  a   |  b   |  c   
------+------+------
    1 |    2 | NULL
    2 |    2 | NULL
    1 |    1 | NULL
    3 | NULL | NULL
    4 | NULL | NULL
 NULL | NULL | 5
(6 rows)

test=# select * from tbl_foreign_refd ;
 a | b |  c   
---+---+------
 1 | 3 | NULL
 2 | 2 | NULL
(2 rows)

對於要增加的外鍵來說,(1,1,NULL),(1,2,NULL),(3,NULL,NULL),(4,NULL,NULL)是臟數據。

第二步:查詢臟數據

test=# select * from tbl_foreign where not exists (select null from tbl_foreign_refd where tbl_foreign_refd.a=tbl_foreign.a and tbl_foreign_refd.b=tbl_foreign.b) and ((a is not null and b is not null) or (a is not null and b is null) or(a is null and b is not null));
 a |  b   |  c   
---+------+------
 1 |    1 | NULL
 1 |    2 | NULL
 3 | NULL | NULL
 4 | NULL | NULL
(4 rows)

第三步:刪除臟數據

將上面SQL語句的SELECT替換成DELETE即可。

test=# delete from tbl_foreign where not exists (select null from tbl_foreign_refd where tbl_foreign_refd.a=tbl_foreign.a and tbl_foreign_refd.b=tbl_foreign.b) and ((a is not null and b is not null) or (a is not null and b is null) or(a is null and b is not null));
DELETE 4

第四步:增加外鍵約束

test=# alter table tbl_foreign add constraint fk_tbl_foreign_a_b foreign key(a,b) references tbl_foreign_refd(a,b) match full;
ALTER TABLE

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM