任务八 测试 openGauss DBMS 的数据库约束
任务目标
机构组织的业务规则,有相当一部分通过数据库约束来实现。通过本任务,读者可以掌握
openGaussDBMS的主键(PRIMARY KEY
)约束、UNIQUE
约束、NOT NULL
约束、外键(FOR- EIGNKEY
)约束、CHECK
约束,并能为某个属性设置默认值(DEFAULT
)。
实施步骤
一、录到数据库studentdb
用Linux用户omm,打开一个Linux终端窗口,执行如下的命令,使用数据库用户student登录到数据库studentdb:
gsql-d studentdb-h 192.168.100.91-U student-p26000-W student@ustb2020-r
二、测试CHECK约束
1.列级CHECK约束
在创建表的时候,创建列级CHECK约束:
DROP TABLE IF EXISTS test;
-- 创建表,age列的值表示活着的人的年龄,是0<=age<200的整数
-- 在列级创建该CHECK约束
CREATE TABLE test(age INT CHECK(age>=0 AND age<200),
sex INT);
INSERT INTO test VALUES(-20,1); -- 插入值不满足CHECK约束,插入报错!
INSERT INTO test VALUES(20,1); -- 插入值满足CHECK约束
INSERT INTO test VALUES(201,1); -- 插入值不满足CHECK约束,插入报错!
2.表级CHECK约束
在创建表的时候,创建表级CHECK约束:
DROP TABLE IF EXISTS test;
-- 创建表,age列的值表示活着的人的年龄,是0<=age<200的整数
-- 在表级创建该CHECK约束
CREATE TABLE test( age INT,
sex INT,
CHECK(age>=0 AND age<200)
);
INSERT INTO test VALUES(-20,1); -- 插入值不满足CHECK约束,插入报错!
INSERT INTO test VALUES(20,1); -- 插入值满足CHECK约束
INSERT INTO test VALUES(201,1); -- 插入值不满足CHECK约束,插入报错!
三、测试NOTNULL约束
在创建表的时候,创建列级NOTNULL约束:
DROP TABLE IF EXISTS test;
CREATE TABLE test( sno int NOT NULL, -- 在列级创建NOT NULL约束
age int CHECK(age>0 and age<200)
);
INSERT INTO test VALUES(1,20); -- 插入的行满足sno列非空
INSERT INTO test VALUES (NULL,20); -- 插入的行不满足sno列非空,出错
四、验证一个属性可以有多个列级约束
创建表的时候,可以同时指定多个列级约束:
DROP TABLE IF EXISTS test;
-- 一个属性在列级可以有多个约束
-- NOT NULL约束
-- CHECK约束
CREATE TABLE test( age INT NOT NULL CHECK(age>0 AND age<200) );
INSERT INTO test VALUES(20); -- 插入的行满足age列非空且满足CHECK约束
INSERT INTO test VALUES(201); -- 插入的行满足age列非空但不满足CHECK约束,出错
五、测试UNIQUE约束
执行下面的语句,测试列级UNIQUE约束:
DROP TABLE IF EXISTS test;
CREATE TABLE test( sno INT,
age INT UNIQUE -- 列级UNIQUE约束
);
INSERT INTO test VALUES(1,20); -- 插入的行满足 age唯一约束
INSERT INTO test VALUES(2,20); -- 插入的行不满足age唯一约束
执行下面的语句,测试表级UNIQUE约束:
DROP TABLE IF EXISTS test;
CREATE TABLE test(
sno int,
col1 int,
col2 int,
UNIQUE(col1,col2) -- 表级多列组合值唯一约束
);
INSERT INTO test VALUES(1,20,20); -- 插入的行 满足多列组合值唯一约束
INSERT INTO test VALUES(2,20,20); -- 插入的行不满足多列组合值唯一约束,出错
六、测试主键约束
执行下面的语句,测试列级主键约束:
DROP TABLE IF EXISTS test;
CREATE TABLE test(
sno int PRIMARY KEY, -- 列级主键约束
age int
);
INSERT INTO test VALUES(1,20); -- 插入的行 满足主键约束
INSERT INTO test VALUES(2,20); -- 插入的行 满足主键约束
INSERT INTO test VALUES(2,20); -- 插入的行不满足主键约束,出错!
七、测试DEFAULT(给属性赋默认值)
DROP TABLE IF EXISTS test;
CREATE TABLE test(
sno int PRIMARY KEY,
age int -- 没有定义该列的默认值,默认值缺省为null
);
INSERT INTO test VALUES(1,20); -- 插入的行提供了所有的值
INSERT INTO test(sno) VALUES(2); -- 插入的行未提供age的值,赋予默认值null
SELECT * FROM test;
执行下面的语句,为某个列(属性)指定默认值:
DROP TABLE IF EXISTS test;
CREATE TABLE test(
sno int PRIMARY KEY,
age int DEFAULT 20 -- 如果没有提供该列的值,赋予默认值20
);
INSERT INTO test VALUES(1,20); -- 插入的行提供了所有的值
INSERT INTO test(sno) VALUES(2); -- 插入的行未提供age的值,赋予默认值20
SELECT * FROM test;
八、测试外键约束
1.测试外键约束的默认行为
外键约束的默认行为是:只能为子表添加这样的行,其外键值是父表主键值集合中的元素;只能删除父表中这些行,其主键值已经不被子表外键所引用。
第一个例子是为子表添加行的例子。水果店出售不同品种的水果,水果店销售的水果只能是水果店进货的水果,没在进货列表的水果,没法记录在销售记录表中。下面的测试说明了这一点:
DROP TABLE IF EXISTS sell;
DROP TABLE IF EXISTS fruitstock;
CREATE TABLE fruitstock ( fruitname varchar(30) PRIMARY KEY);
CREATE TABLE sell( sellno INT PRIMARY KEY,
fruitname varchar(30) REFERENCES fruitstock(fruitname)
);
-- 水果店只卖apple、banana和pear
-- 往表fruitstock插入水果apple、banana和pear
insert into fruitstock values('apple');
insert into fruitstock values('banana');
insert into fruitstock values('pear');
-- 表sell记录销售情况,只要销售的水果名字都是表fruitstock记录的,就满足外键约束
INSERT INTO sell (sellno, fruitname) VALUES (1, 'apple'), (2, 'apple'), (3, 'banana'), (4, 'banana'), (5, 'pear'), (6, 'pear');
-- 往子表sell插入的行,水果名如果没有记录在表fruitstock中,不满足外键约束
-- 父表fruitstock的fruitname列中没有值为mongo的行
insert into sell values(7,'mango');
第二个例子是删除行的例子。我们想删除父表中的水果pear,但是由于子表中还有关于pear的销售记录,因此无法删除父表中名为pear的记录。
-- 删除父表fruitstock的pear,因为子表sell还包含值为pear的行,违背外键约束
-- 因此无法删除父表中的记录pear
delete from fruitstock where fruitname='pear';
如果我们先删除子表中外键值为pear的行,那么在这些行被删除之后,就可以删除父表fruitstock中包含pear值的记录了。读者可以自己完成这个操作。
2.测试外键约束的on delete cascade特性
查看表sell上的约束(查看表的信息就可以看到该表上的约束情况):
\d sell
若要修改表约束,只能先删除约束然后再重建约束。下面将约束特性修改为on delete cascade和 on update cascade:
\set AUTOCOMMIT off
alter table sell drop constraint sell_fruitname_fkey;
alter table sell add constraint sell_fruitname_fkey foreign key (fruitname)
references fruitstock(fruitname) on delete cascade on update cascade;
commit;
约束具有on delete cascade特性,意味着如果删除父表的记录,同时会将子表的对应记录页删除。下面的例子中,删除父表fruitstock中的pear记录,子表sell中关于水果pear的相应记录也会被删除:
如果读者尝试了删除父表fruitstock中包含pear值的记录了,那么可以使用下面的语句加入:
INSERT INTO fruitstock VALUES('pear');
INSERT INTO sell VALUES(5, 'pear');
INSERT INTO sell VALUES(6, 'pear');
commit;
以看出,约束特性是ondeletecascade时,在删除父表fruitstock中的记录后,会把子表中的相应记录级联删除。最后执行回滚语句,使测试数据恢复原状,以便继续进行下面的测试。
3.测试外键约束的on update cascade特性
下面测试外键约束的on update cascade特性:
4.测试外键约束的on delete set null特性
首先修改外键约束特性为on delete set null和on update set null:
\set AUTOCOMMIT off
alter table sell drop constraint sell_fruitname_fkey;
alter table sell add constraint sell_fruitname_fkey foreign key (fruitname)
references fruitstock(fruitname) on delete set null on update set null;
commit;
select * from fruitstock;
select * from sell;
-- 外键约束具有on delete set null 特性,因此可以删除父表fruitstock的pear,
-- 但是会在删除父表相关记录的同时,将子表sell中外键的相应值设置为null
delete from fruitstock where fruitname='pear';
select * from fruitstock;
select * from sell;
rollback;
5.测试外键约束的on delete set null特性
下面测试外键约束的on delete set null特性。将父表fruitstock中的pear更新为grape:
select * from fruitstock;
select * from sell;
-- 外键约束具有on update set null 特性,因此可以更新父表fruitstock的pear值为grape,
-- 但在更新父表相关记录的同时,会将子表sell中外键的相应值设置为null
update fruitstock set fruitname='grape'where fruitname='pear';
select * from fruitstock;
select * from sell;
rollback;
九、查看表的约束
使用\d tableName命令查看表及其上的约束信息:
\d instructor
十、任务的扫尾工作
DROP TABLE IF EXISTS test;
DROP TABLE IF EXISTS sell;
DROP TABLE IF EXISTS fruitstock;
\q