oracle数据库:约束
Posted 谦谦均
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了oracle数据库:约束相关的知识,希望对你有一定的参考价值。
约束简介
约束是数据库用来确保数据满足业务规则的手段,不过在真正的企业开发中,除了主键约束这类具有强需求的约束,像外键约束,检查约束更多时候仅仅出现在数据库设计阶段,真实环境却很少应用,更多是放到程序逻辑中去进行处理。这也比较容易理解,约束会一定程度上较低数据库性能,有些规则直接在程序逻辑中处理就可以了,同时,也有可能在面对业务变更或是系统扩展时,数据库约束会使得处理不够方便。不过在我看来,数据库约束是保证数据准确性的最后一道防线,对于设计合理的系统,处于性能考虑数据库约束自然可有可无;不过若是面对关联关系较为复杂的系统,且对系统而言,数据的准确性完整性要高于性能要求,那么这些约束还是有必要的,否则,就会出现各种相对业务规则来说莫名其妙的脏数据。总之,对于约束的选择无所谓合不合理,需要根据业务系统对于准确性和性能要求的侧重度来决定。
oracle数据库的约束有五种:
- 主键约束(
primary key
) - 唯一约束(
unique
) - 非空约束(
not null
) - 外键约束(
foreign key
) - 检查约束(
check
)
1,主键约束 primary key
主键是定位表中单个行的方式,可唯一确定表中的某一行,关系型数据库要求所有表都应该有主键,不过oracle数据库没有遵循这个规范,oracle
中的表可以没有主键(这种情况不多见),关于主键有几个需要注意的点:
- 键列必须具有唯一性,且不能为空,其实主键约束相当于
unique
+not null
- 一个表中只允许有一个主键
- 主键所在列必须具有索引(主键的唯一约束通过索引来实现),如果不存在,将会在索引添加的时候自动创建。
2,唯一约束 unique
唯一约束可作用在单列或者多列上,对于这些列或者列组合,唯一性约束保证每一行的唯一性。
唯一性约束有以下几个注意点:
- 对于
unique
来说,所以是必须的。如果不存在,就自动创建一个(unique
的唯一性本质上是通过索引来保证的) unique
允许存在null
值,unique
约束的列可以存在多个null
,这是因为,unique
唯一性通过btree
索引来实现,而btree
索引中不包含null
。当然,这也造成了在where
语句中使用null
值进行过滤会造成全表扫描。
3,非空约束 not null
非空约束作用的列也叫强制列。顾名思义,非空列中必须要有值,当然建表的时候如果使用default
关键字指定了默认值,则可以不输入。
4,外键约束 foreign key
外键约束定义在具有父子关系的子表中,外键约束使得子表中的列对应父表的主键列,用以维持数据库的完整性。不过出于性能和后期的业务系统扩展的考虑,很多时候,外键约束仅仅出现在数据库的设计中,实际会放在业务程序中进行处理。外键约束要注意以下几点:
- 外键约束的子表中的列和对应父表中的列数据类型必须相同,列名可以不同
- 对应的父表列必须存在主键约束(
primary key
)或者唯一约束(unique
) - 外键约束允许列存在
null
值,对应的行就成了孤行。
其实很多时候不使用外键,很多人认为会让删除操作变得比较麻烦,比如要删除父表中的某条数据,但是某个子表中又有对该条数据的引用,这时就会导致删除失败。这种情况下有几种方式来优化这个场景:
- 第一种方式简单粗暴,删除的时候,级联删除掉子表中的所有匹配行,在创建的时候,通过
on delete cascade
子句指定该外键列可以级联删除。 - 第二种方式,删除父表中的对应行,会将对应子表中的所有匹配行的外键约束列置为
null
,通过on delete set null
子句实现 - 第三种,默认,强制不让删
5,检查约束 check
检查约束可以用来实施一些简单的规则,比如列值必须在某个范围内,检查的规则必须是一个结果为true
或false
的表达式。
6,创建表时创建约束
这里以创建下面两张比表为例子
表名:w_user
用户表
编号 | 字段名 | 字段类型 | 说明 |
---|---|---|---|
1 | userid | number(5) | 用户id,主键约束 |
2 | username | varchaer2(30) | 用户名,非空约束,4~20个字符 |
3 | userpwd | varchar(20) | 密码,非空约束,4~18个字符 |
4 | age | number(3) | 年龄,默认18,值大于等于18 |
5 | gender | char(2) | 性别,默认‘男’,只能是‘男’或者‘女’ |
6 | varchar2(30) | 邮箱,唯一约束 | |
7 | regtime | date | 注册日期,默认当前日期 |
表名:w_txt
文章表
编号 | 字段名 | 字段类型 | 说明 |
---|---|---|---|
1 | txtid | number(5) | 文章编号,主键约束 |
2 | title | varchar2(32) | 文章标题,非空约束,长度4~20字符 |
3 | txt | varchar2(1024) | 内容,最大长度1024 |
4 | pubtime | date | 发布日期,默认当前日期 |
5 | userid | number(5) | 作者,外键约束,参考用户表的用户id,删除时,自设为null |
创建w_user
表:
--创建w_user表,同时加上约束,不设置约束名称
create table w_user(
userid number(5) primary key,--主键,唯一且非空
username varchar2(30) not null check(length(username ) between 4 and 20),--检查约束和非空约束
userpwd varchar2(20) check(length(userpwd) between 4 and 18) not null,--检查约束和非空约束
age number(3) default(18) check(age>=18),--设置默认值和检查约束
gender char(4) default('男') check(gender in('男','女')),--默认值和检查约束
email varchar2(30) unique,--唯一约束
regtime date default(sysdate) --默认系统当前时间
);
执行之后找到创建好的表,打开查看结构【客户端用的PLSQL
】:
点击检查可以查看设置的检查约束
接下来创建第二张表:
--创建w_txt表
create table w_txt(
txtid number(5) primary key,
title varchar2(32) not null check(length(title)>=4 and length(title)<=30),
txt varchar2(1024),
pubtime date default(sysdate),
userid number(5) references w_user(userid) on delete set null
);
最后一个字段是外键,这里on delete set null
意思是当w_user
表里面的某个userid
删除之后,这个表里面对应的设置为null
。
打开表看一下结构如上图所示,这里主要看一下外键约束。
7,创建表时,创建带名字的约束
在创建上述两张表的时候并没有指定对应的约束名称,这样当我们后期对表进行一些操作的时候,如果违反了某个约束,报错的时候提示的是系统默认的约束名称,很不方便我们找到错误,因此可以直接给约束指定名称。
这里先把刚才的表删除,或者把下面创建的表的名字改一下
--删除表
drop table w_txt;
drop table w_user;
创建约束带有名字的w_user
表
--创建约束带有名字的w_user表
create table w_user(
userid number(5),
username varchar2(30) constraint nn_user_name not null,--检查约束和非空约束
userpwd varchar2(20) constraint nn_user_pwd not null,--检查约束和非空约束
age number(3) default(18),--设置默认值和检查约束
gender char(4) default('男'),--默认值和检查约束
email varchar2(30),--唯一约束
regtime date default(sysdate),
constraint pk_user_id primary key(userid),--给userid追加主键约束
constraint ck_user_name check(length(username) between 4 and 20),--给username追加check约束
constraint ck_user_pwd check(length(userpwd) between 4 and 18),--给userpwd追加check约束
constraint ck_user_age check(age>=18),--给age追加check约束
constraint ck_user_gender check(gender in('男','女')),--给gender追加check约束
constraint ck_uaer_email unique(email)--给email追加唯一约束
);
创建约束带有名字的w_txt
表
--创建约束带有名字的w_txt表
create table w_txt(
txtid number(5),
title varchar2(32) constraint nn_txt_title not null,
txt varchar2(1024),
pubtime date default(sysdate),
userid number(5),
constraint pk_txt_id primary key(txtid),
constraint ck_txt_title check(length(title)>=4 and length(title)<=30),
constraint fk_txt_user_id foreign key (userid) references w_user(userid) on delete set null
);
创建之后查询能看到跟刚才一样,并且这是大部分约束都起了名字,default
一般不用起名字。
8,创建表之后追加约束
有时候我们创建表的时候没考虑太多,创建好之后才发现某些字段需要加上约束,这时候可以使用追加约束的sql语句实现功能。
还是接上面的表,先把之前的表删除(因为创建的表内容都一样),也可以给接下来创建的表改个名字,创建时不报错就行。
--删除表
drop table w_txt;
drop table w_user;
创建w_user
表:
--创建w_user表 不带约束
create table w_user(
userid number(5),
username varchar2(30),
userpwd varchar2(20),
age number(3),
gender char(4),
email varchar2(30),
regtime date
);
创建的这个表一个约束都没有,看一下表的结构:
其中键和检查这两页都是空白【这里就不截图了】,接下来给这个表添加之前的那些约束:
--给表添加约束
--userid追加主键
alter table w_user add constraint pk_user_id primary key(userid);
--username追加非空和check约束
alter table w_user modify(username constraint nn_user_name not null);--非空约束
alter table w_user add constraint ck_user_name check(length(username) between 4 and 20);--检查约束
--userpwd追加非空约束和check约束
alter table w_user modify(userpwd constraint nn_user_pwd not null);--非空约束
alter table w_user add constraint ck_user_pwd check(length(userpwd) between 4 and 18);--检查约束
--age追加check约束和默认值
alter table w_user add constraint ck_user_age check(age>=18);--check约束
alter table w_user modify(age default(18));--追加默认约束
--gender追加check和default
alter table w_user add check(gender in('男','女'));--check约束
alter table w_user modify(gender default('男'));--追加默认约束
--email追加唯一约束
alter table w_user add constraint uq_uaer_email unique(email);--唯一约束
--regtime追加默认值
alter table w_user modify(regtime default(sysdate));--追加默认约束
执行之后查看表的结构:
再看一下检查界面
接下来给w_txt
创建没有约束的表,以及追加约束:
--创建w_txt表,不添加约束
create table w_txt(
txtid number(5),
title varchar2(32),
txt varchar2(1024),
pubtime date,
userid number(5)
);
同上w_user
表,现在没有添加任何的约束,接下来给表中追加约束:
--创建w_txt表,不添加约束
create table w_txt(
txtid number(5),
title varchar2(32),
txt varchar2(1024),
pubtime date,
userid number(5)
);
--给w_txt追加约束
--txtid添加主键约束
alter table w_txt add constraint pk_txt_id primary key(txtid);--主键约束
--title添加非空约束以及check约束
alter table w_txt modify(title constraint nn_txt_title not null);--非空约束
alter table w_txt add constraint ck_txt_title check(length(title)>=4 and length(title)<=30);--检查约束
--pubtime设置默认值
alter table w_txt modify(pubtime default(sysdate));--追加默认约束
--userui设置外键约束
--方法1:强制不让删 【文章表的内容没删除干净之前,用户表的内容不让删】
alter table w_txt add constraint fk_txt_user_id foreign key (userid) references w_user(userid);--强制不让删
--方法2:自动设置为null 【如果文章没有删除,然后用户id删除了,那么文章的关联id自动设置为null】
alter table w_txt add constraint fk_txt_user_id foreign key (userid) references w_user(userid) on delete set null;--自动设置为null
--方法3:级联删除 【当用户表的id删除时,文章表对应的内容一起删除】
alter table w_txt add constraint fk_txt_user_id foreign key (userid) references w_user(userid) on delete cascade;--级联删除
添加外键有三种方式,之前设置的是自动设置为空,这里运行的是设置为空,三种方法是选一种运行,同时运行多个会报错的。看一下表的结构:
看一下检查界面:
看一下键界面:
9,禁用和启用约束
很多时候由于业务需要,比如我们有大量的历史数据,需要和现有数据合并,当前表存在数据库约束(如非空约束),而这些历史数据又包含违背非空约束的数据行,为了避免导入时由于违反约束而导入失败,我们通过调整约束状态来达到目的。
数据库约束有两类状态
- 启用/禁用(
enable/disable
):是否对新变更的数据启用约束验证 - 验证/非验证(
validate/novalidate
):是否对表中已客观存在的数据进行约束验证
这两类四种状态从语法角度讲可以随意组合,默认是enable validate
下面我们来看着四类组合会分别出现什么样的效果:
enable validate
:默认的约束组合状态,无法添加违反约束的数据行,数据表中也不能存在违反约束的数据行;enable novalidate
:无法添加违反约束的数据行,但对已存在的违反约束的数据行不做验证;disable validate
:可以添加违反约束的数据行,但对已存在的违反约束的数据行会做约束验证(从描述中可以看出来,这本来就是一种相互矛盾的约束组合,只不过是语法上支持这种组合罢了,造成的结果就是会导致DML失败)disable novalidate
:可以添加违法约束的数据行,对已存在的违反约束的数据行也不做验证。
拿上面的例子来说,我们需要上传大量违反非空约束的历史数据(从业务角度讲这些数据不会造成系统功能异常),可以临时将约束状态转为disable novalidate
,以保证这些不合要求的数据导入表中。
举个例子:
alter table w_user add primary key(userid) disable;--禁用主键
alter table w_user modify primary key(userid) enable novalidate;--启用主键并验证
10,删除约束
语法:
alter table 表名 drop constraint 约束名 cascade;
从上面的语法结构中可知,要删除约束就要知道对应约束的名称,所以给约束起名字是一个很好的习惯。
alter table w_user drop constraint uq_user_email cascade;
这个sql
语句最后的cascade
是级联删除,意思是把跟这个约束相关的约束一起删除。
以上是关于oracle数据库:约束的主要内容,如果未能解决你的问题,请参考以下文章