oracle数据库:约束

Posted 谦谦均

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了oracle数据库:约束相关的知识,希望对你有一定的参考价值。

约束简介

约束是数据库用来确保数据满足业务规则的手段,不过在真正的企业开发中,除了主键约束这类具有强需求的约束,像外键约束,检查约束更多时候仅仅出现在数据库设计阶段,真实环境却很少应用,更多是放到程序逻辑中去进行处理。这也比较容易理解,约束会一定程度上较低数据库性能,有些规则直接在程序逻辑中处理就可以了,同时,也有可能在面对业务变更或是系统扩展时,数据库约束会使得处理不够方便。不过在我看来,数据库约束是保证数据准确性的最后一道防线,对于设计合理的系统,处于性能考虑数据库约束自然可有可无;不过若是面对关联关系较为复杂的系统,且对系统而言,数据的准确性完整性要高于性能要求,那么这些约束还是有必要的,否则,就会出现各种相对业务规则来说莫名其妙的脏数据。总之,对于约束的选择无所谓合不合理,需要根据业务系统对于准确性和性能要求的侧重度来决定。

oracle数据库的约束有五种:

  • 主键约束(primary key
  • 唯一约束(unique
  • 非空约束(not null
  • 外键约束(foreign key
  • 检查约束(check

1,主键约束 primary key

主键是定位表中单个行的方式,可唯一确定表中的某一行,关系型数据库要求所有表都应该有主键,不过oracle数据库没有遵循这个规范,oracle中的表可以没有主键(这种情况不多见),关于主键有几个需要注意的点:

  1. 键列必须具有唯一性,且不能为空,其实主键约束相当于unique+not null
  2. 一个表中只允许有一个主键
  3. 主键所在列必须具有索引(主键的唯一约束通过索引来实现),如果不存在,将会在索引添加的时候自动创建。

2,唯一约束 unique

唯一约束可作用在单列或者多列上,对于这些列或者列组合,唯一性约束保证每一行的唯一性。
唯一性约束有以下几个注意点:

  • 对于unique来说,所以是必须的。如果不存在,就自动创建一个(unique的唯一性本质上是通过索引来保证的)
  • unique允许存在null值,unique约束的列可以存在多个null,这是因为,unique唯一性通过btree索引来实现,而btree索引中不包含null。当然,这也造成了在where语句中使用null值进行过滤会造成全表扫描。

3,非空约束 not null

非空约束作用的列也叫强制列。顾名思义,非空列中必须要有值,当然建表的时候如果使用default关键字指定了默认值,则可以不输入。

4,外键约束 foreign key

外键约束定义在具有父子关系的子表中,外键约束使得子表中的列对应父表的主键列,用以维持数据库的完整性。不过出于性能和后期的业务系统扩展的考虑,很多时候,外键约束仅仅出现在数据库的设计中,实际会放在业务程序中进行处理。外键约束要注意以下几点:

  1. 外键约束的子表中的列和对应父表中的列数据类型必须相同,列名可以不同
  2. 对应的父表列必须存在主键约束(primary key)或者唯一约束(unique
  3. 外键约束允许列存在null值,对应的行就成了孤行。

其实很多时候不使用外键,很多人认为会让删除操作变得比较麻烦,比如要删除父表中的某条数据,但是某个子表中又有对该条数据的引用,这时就会导致删除失败。这种情况下有几种方式来优化这个场景:

  1. 第一种方式简单粗暴,删除的时候,级联删除掉子表中的所有匹配行,在创建的时候,通过on delete cascade 子句指定该外键列可以级联删除。
  2. 第二种方式,删除父表中的对应行,会将对应子表中的所有匹配行的外键约束列置为null,通过on delete set null子句实现
  3. 第三种,默认,强制不让删

5,检查约束 check

检查约束可以用来实施一些简单的规则,比如列值必须在某个范围内,检查的规则必须是一个结果为truefalse的表达式。

6,创建表时创建约束

这里以创建下面两张比表为例子
表名:w_user 用户表

编号字段名字段类型说明
1useridnumber(5)用户id,主键约束
2usernamevarchaer2(30)用户名,非空约束,4~20个字符
3userpwdvarchar(20)密码,非空约束,4~18个字符
4agenumber(3)年龄,默认18,值大于等于18
5genderchar(2)性别,默认‘男’,只能是‘男’或者‘女’
6emailvarchar2(30)邮箱,唯一约束
7regtimedate注册日期,默认当前日期

表名:w_txt 文章表

编号字段名字段类型说明
1txtidnumber(5)文章编号,主键约束
2titlevarchar2(32)文章标题,非空约束,长度4~20字符
3txtvarchar2(1024)内容,最大长度1024
4pubtimedate发布日期,默认当前日期
5useridnumber(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数据库:约束的主要内容,如果未能解决你的问题,请参考以下文章

在约束布局中查看片段的绑定不起作用

Oracle数据库对象_约束

sql Oracle代码片段

Oracle中的约束

启用 oracle 约束的问题

oracle 删除外键约束 禁用约束 启用约束