赞
踩
前面聊增删改的时候,说到了约束,本章就这个问题聊一下,其实约束很多时候都是再创建的时候就设置或者添加的,毕竟约束毕竟对数据影响还是很大的。
数据的完整性是指数据的精确性以及可靠性。他是房主数据库中存在不符合予规范的数据和防止因错误信息的输入输出造成的无效操作和错误信息而提出的。
前面聊数据类型的时候,可以看出在创建数据库的时候考虑好存储的数据类型以及数据长度,但是为了数据的完整性,SQL规范以约束的方式对表的数据进行了额外的条件限制。需要从四个方面考虑:
其实约束的本质就是保证数据的完整性。
约束时表级的强制规定。可以在创建表时规定约束(CREATE TABLE)或者通过ALTER 语句规定约束。
但是一般约束都是在创建表时候就添加了,后期能改但是一般会很少改。
分类也是根据不同的标准分不同的类别。
根据约束数据列的限制:单列约束和多列约束
根据约束的作用范围:列级约束和表级约束
根据约束作用:非空约束,唯一约束等
下面演示的话,我们根据约束种类进行演示,其它的分类也会捎带讲解。
MYSQL方便用户对自己设置的约束进行查看,自然也会有SQL语句查看:
SELECT * FROM information_schema.TABLE_CONSTRAINTS WHERE table_name='表名'
这个就是字面上的意思,限制表的某个字段不能为空,也就是说必须有值,比如员工统计的员工号或者身份证号等。
因为MYSQL默认所有类型都可以为NULL,费用约束只能出现在表对象的列上,只能为列单独限定非空,不能组合非空。一个表可以有很多列分别限定了非空,补充说一些空字符串不但能与NULL,0也不等于null。
格式:
-- 创建表的时候加入非空约束
CREATE TABLE 表的名称(
字段1 数据类型 NOT NULL,
字段2 数据类型,
[字段1 数据类型 NOT NULL]
-- 可以多个列设置不可为空,不为空的列可以在任何一个字段
)
现在举例:
-- 先声明一下 创建表没有什么意义,只是为了演示效果
CREATE TABLE test(
a INT NOT NULL,
b VARCHAR(10),
c DATE
)
这个是在创建表的添加了约束,也是一个单列约束,毕竟只对一个列生效,而且非空限制对本身就无法多列限制。
然后插入数据:
INSERT INTO test (a,b,c) VALUES (1,'abc','2001-01-01');
INSERT INTO test (a,b,c) VALUES (2,NULL,'2002-01-01');
INSERT INTO test (a,c) VALUES (3,'2003-01-01')
如果插入数据a字段为空呢?
INSERT INTO test (a,b,c) VALUES (NULL,'bcd','2002-01-01');
就会报错提示该字段无法存储空值,当然为了数据库兼容性更高,这个时候会设置一个默认值,这个关键字下面聊。
还有可以尝试以下,如果修改a的值为空是否可行?
UPDATE test SET a=NULL WHERE a='1'
如果是低版本比如5.0会报一个警告
但是会强制将空变成0
如果是8.0,以及5.7直接回报错:
如果在创建表的时候,忘了加非空约束,可以通过
ALTER TABLE test
MODIFY 字段名 字段类型 NOT NULL
演示:
这个需要确定一件事情,那就是是否要上约束的字段是否本身就有空;如果有空的话?
如果字段本身数据没有空值直接用就好,如果有空值不同的版本有不同的结果
ALTER TABLE test
MODIFY b VARCHAR(10) NOT NULL
先看5.0 可以执行,但是又警告,以及空值强转
但是8.0版本的却直接报错
删除的话,其实和建表后添加一样
ALTER TABLE test
MODIFY 字段名 字段类型
这个就不在演示了。
这个对于某些字段和某列的值不能重复,比如身份证号或者员工号不能重复。
唯一约束特点:
在创建表的时候添加唯一约束。
--这个是列约束写法 CREATE TABLE 表名( 字段1 类型 UNIQUE, ......... , -- 可以多个列设置为唯一,唯一的列可以在任何一个字段 ) -- 表约束写法 CREATE TABLE 表名( 字段1 字段类型, 字段2 字段类型, [CONSTRAINT 唯一约束名] UNIQUE(字段1,....) -- 中括号内的内容 可以取消,唯一约束名默认是字段名 -- 前面的非空约束支持表约束 -- 表约束的时候 如果放入多个字段,就是多列约束 )
演示
CREATE TABLE test(
a INT UNIQUE,-- 列约束
b VARCHAR(10),
c VARCHAR(10),
d DATE,
e INT,
CONSTRAINT bc UNIQUE (b),-- 表约束
UNIQUE (c,d) -- 表约束中的多列约束
)
通过DESC查看表
可以看出多列创建的唯一索引,却是MUL。
现在通过sql语句查询约束:
SELECT * FROM information_schema.TABLE_CONSTRAINTS WHERE table_name='test'
现在开始插入数据
INSERT INTO test (`a`, `b`, `c`, `d`, `e`) VALUES ('1', 'ab', 'ab', '2021-01-01', '1') ;
然后再插入一个数据:
INSERT INTO test (`a`, `b`, `c`, `d`, `e`) VALUES ('1', 'ab1', 'ab1', '2021-01-02', '1') ;
可以看出直接报错,因为约束了a不可以重复,,记住一点这个不可重复是本字段的数据不可重复,而不是其它列不能存储相同数据。
INSERT INTO test (`a`, `b`, `c`, `d`, `e`) VALUES (NULL, 'ab1', 'ab1', '2021-01-02', '2') ;
INSERT INTO test (`a`, `b`, `c`, `d`, `e`) VALUES (NULL, 'ab2', 'ab2', '2021-01-03', '2') ;
但是插入a为空的两条数据不会报错,因为a不重复的数据不包括NULL。
如果更新某个定义唯一的值的时候,要看是否有重复的值,比如上面的表:
UPDATE test SET a='1' WHERE d ='2021-01-03';
-- 这个不会成功会报错的。
为了方便演示,所以将a和b的值插入NULL ,然后体验以下多列约束(也成为复合约束)。
INSERT INTO test (`a`, `b`, `c`, `d`, `e`) VALUES (NULL, NULL, 'abc', '2021-01-04', '2') ;
-- 如果插入两次就会报错
INSERT INTO test (`a`, `b`, `c`, `d`, `e`) VALUES (NULL, NULL, 'abc', '2021-01-04', '2') ;
现在修改一个值:
INSERT INTO test (`a`, `b`, `c`, `d`, `e`) VALUES (NULL, NULL, 'abc1', '2021-01-04', '2') ;
可以看出如果多列的唯一约束,只要一个列修改,就可以插入,当然如果其中一个值是NULL,也是可以多次插入的:
INSERT INTO test (`a`, `b`, `c`, `d`, `e`) VALUES (NULL, NULL, NULL, '2021-01-05', '2') ;
INSERT INTO test (`a`, `b`, `c`, `d`, `e`) VALUES (NULL, NULL, NULL, '2021-01-05', '2') ;
格式:
-- 格式 方式1
ALTER TABLE 表名
ADD CONSTRAINT 唯一约束名 UNIQUE(字段);
-- 方式2
ALTER TABLE 表名
MODIFY 字段名 字段类型 UNIQUE;
这个后期修改的话,也是要注意要修改的列中的数据会不会有重复的,不然也会报错。所以为什么说要在建表的时候设置而不是后期修改了,这个就不在演示了。
当然如果修改一个唯一约束和其它的列在组成一个复合唯一约束也是可以,比如:
ALTER TABLE test
ADD CONSTRAINT aa UNIQUE(a,e)
这个可能有点复杂,先了解一个特性。
-- 通过删除索引刹出唯一约束
ALTER TABLE test
DROP INDEX 唯一约束名
索引的问题,后面优化的时候再聊,现在知道这个概念即可。
主键其时用来唯一标识表中的一行记录。主键约束相当于唯一约束+非空约束的组合,其不允许重复,也不允许出现空值。
当然其也有自己的特点:
添加主键,还是老规矩,先用创建表的时候添加
-- 列级主键约束 CREATE TABLE 表名( 字段 字段类型 PRIMARY KEY, 字段 字段类型 , .... -- 主键最多就一个,一般放在第一个字段。 ) -- 表级 主键约束 CREATE TABLE 表名( 字段 字段类型, 字段 字段类型 , ...., [CONSTRAINT 主键名设置也白设置 ] PRIMARY KEY (字段) -- 主键最多就一个,一般放在第一个字段。 )
现在
CREATE TABLE test(
a INT PRIMARY KEY,
b VARCHAR(10),
c DATE
)
现在看一下其主键名:
SELECT * FROM information_schema.`TABLE_CONSTRAINTS` WHERE table_name='test';
可以看出其约束的名字时 PRIMARY,用表级添加一个名字但是还是PRIMAYR。
INSERT INTO `test`.`test` (`a`, `b`, `c`) VALUES (1,'a','2021-01-02');
-- 然后再插入这一条数据;
如果单独这样看似乎和唯一约束没有什么区别,现在尝试插入一个主键为NULL。
INSERT INTO `test`.`test` (`a`, `b`, `c`) VALUES (NULL,'a','2021-01-02');
这个可以看出主键无法插入NULL。
现在用表级的创建一个多列主键约束。
CREATE TABLE test1(
a INT,
b INT,
c DATE ,
CONSTRAINT da_test PRIMARY KEY(a,b)
)
现在看一下主键名:
可以看出主键的默认名是无法修改的。
INSERT INTO `test`.`test1` (`a`, `b`, `c`) VALUES (1,2,'2021-01-02');
--然后再插入一遍
可以看出会报错提示,两者字段组合的值被其它用了。为什么设置两个值都是数值呢,如果翻过是否可以用:
INSERT INTO `test`.`test1` (`a`, `b`, `c`) VALUES (2,1,'2021-01-02');
可以看出复合约束中如果值前后顺序不一样也不会报错,现在插入空试试。
INSERT INTO `test`.`test1` (`a`, `b`, `c`) VALUES (3,NULL,'2021-01-02');
可以看出复合主键约束,其中任意一个字段都不可以为空。
当然还是老规矩,如果再修改的时候要注意数据是否满足其称为主键的条件,比如是否有空,是否又重复的值。
-- 格式1 可以多列和单列
ALTER TABLE 表名
ADD PRIMARY KEY(字段)
-- 格式2
ALTER TABLE 表名
MODIFY 字段 字段类型 PRIMARY KEY
很简单 就不在演示了。
因为每个表只有一个主键,所以删除的时候格式如下:
ALTER TABLE 表名
DROP PRIMARY KEY
但是要补充删除了主键约束,但是非空还是存在的
,如下
ALTER TABLE test1
DROP PRIMARY KEY
说实话主键一般不会删除,会涉及到表的结构(B+TREE,有时间聊)
某个字段的值自增,这个列就被称为为自增列。而这个自增列自字段必须是数值型的主键约束或者唯一约束。
特征:
在创建的时候,添加自增属性。
CREATE TABLE test(
a INT UNIQUE AUTO_INCREMENT,
b VARCHAR
)
然后插入数据
INSERT INTO `test`.`test` ( `b`) VALUES( 'a') ;
INSERT INTO `test`.`test` ( `b`) VALUES( 'b') ;
可以看出a的这一列会自动添加数。再做一个实验。
INSERT INTO `test`.`test` ( `a`, `b`) VALUES( 5, 'c') ;
INSERT INTO `test`.`test` ( `b`) VALUES( 'd') ;
可以看出这个如果直接赋值也是可以的,现在又有了一个大胆的想法。
delete from test;--删除表中数据 然后再插入数据
INSERT INTO `test`.`test` ( `b`) VALUES( 'a') ;
猜测一下结果,会不会还是从1开始?
可以看出是从删除前的数据自增列最大值+1开始。
在做一个实验
-- 如果插入要给值为1呢?
INSERT INTO `test`.`test` ( `a`, `b`) VALUES( 1, 'c') ;
可见是可以的,毕竟数据已经删除,不会被唯一约束影响。
现在再插入1个
INSERT INTO `test`.`test` ( `b`) VALUES( 'd') ;
神奇不,还是不是从2开始,自动添加为8;而且数据还是会自动排序的。
-- 格式 不能用如添加主键那样 add添加
ALTER TABLE 表名
MODIFY 字段名 数值类型 [一般] auto_increment;
-- 格式
ALTER TABLE 表名
MODIFY 字段名 数值类型 ;
--MODIFY 字段名和类型这样即可 不会影响主键或者唯一的约束
这先看现象,比如在8.0版本一下的msyql中演示
CREATE TABLE test(
a INT UNIQUE AUTO_INCREMENT
);
insert into test values (0);
insert into test values (0);
insert into test values (0);
然后再删除表中内容:
delete from test;
前面说过如果插入数据的时候不直接输入定值,就会自动从4开始添加数据。但是现在我们先不添加,而是先重启数据库
-- 重启方式1:
命令方式: net stop mysql
net start mysql
-- 方式2
再服务中右键
-- 方式3
直接重启电脑
现在再向里面插入数据:
insert into test values (0);
insert into test values (0);
下意识认为是会从4开始,但是:
然后再用8.0尝试一下:
还是上面的操作啊,就不在一步一步的演示了,而是直接看结果。不过如果用命令重启8.0的话如下:
命令方式: net stop mysql80
net start mysql80
可以看出不同了,所以说mysql8.0是持久化的,具体原因呢?
限定某个表的某个字段的引用完整性,简单的说某个A表的某些字段关联到B表的某个字段,而A表整个值被B中内容的范围限制。
用例子举例,比如学生选课表和课程表,学生能选的课必须是再课程表中又的学科,不然你学啥?还有客人点菜和菜单表一样,你不能点菜单没有的菜。
这个时候就要引入两个表的概念 主表(父表)和从表(子表)
;
其也有特点:
再创建表的时候添加外键约束
-- 首先需要有一个主表
CREATE TABLE 主表名(
字段1 字段类型 PRIMARY KEY,
字段2 字段类型,
.........
)
--创建时一个从表
CREATE TABLE 从表名(
字段1 字段类型 ,
字段2 字段类型,
.........
CONSTRAINT 外键名 FOREIGN KEY (从表的字段) REFERENCES 主表名(主表中字段如果不知主键或者唯一约束就会报错)
)
现在开始演示:
CREATE TABLE table_a( a_id INT PRIMARY KEY, a_name VARCHAR(2) ); CREATE TABLE table_b( a_id INT, b_id INT, b_name VARCHAR(10), CONSTRAINT b_a FOREIGN KEY (a_id) REFERENCES table_a(a_id) ); -- 插入数据 INSERT INTO table_a VALUES (1,'语文'); INSERT INTO table_a VALUES (2,'数学'); INSERT INTO table_a VALUES (3,'英语'); INSERT INTO test.table_b VALUES ('1', '1', '张三'); INSERT INTO test.table_b VALUES ('2', '1', '张三'); INSERT INTO test.table_b VALUES ('2', '2', '李四');
这个时候插入一个主表不存在的值试试。
INSERT INTO test.table_b VALUES ('4', '2', '李四');
可见会报错的。当然如果更新从表将其a_id变成主表中不存在的值,也会报错的,这个就不在演示了,很容易理解了。
现在删除主表的值试一下。
DELETE FROM test.table_a WHERE a_id = '1' ;
删除主表中的某个被从表引用的数据也会报错,上面数据中a_id中的1被引用了,所以会报错,但是如果删除其=‘3’,就不会报错。这个就说明了如果要删除主表的数据,如果没有被引用还好,如果被引用要不删或修改从表的引用值,或者删除外键约束。
前提时两者的主表中的被引用的列,以及确定时主键或者唯一约束后:
ALTER TABLE 表名
ADD CONSTRAINT 外键名 FOREIGN KEY (从表的字段) REFERENCES 主表名(主表中字段如果不知主键或者唯一约束就会报错)
其实这个有点繁琐具体步骤如下
-- 第一步: 查看约束名
SELECT * FROM information_schema.TABLE_CONSTRAINTS WHERE table_name='表名';
ALTER TABLE 从表
DROP FOREIGN KEY 外键约束名
-- 第二步 因为会自动带一个索引 删除索引
SHOW INDEX FORM 表名;
ALTER TABLE 从表
DROP INDEX 索引名
如果有类员工表和部门表是否一定要建立外键约束,有些时候看公司要求,毕竟有些公司要求不创建,毕竟再增删改的时候会收到约束,但是其数据的完整性得到保证,但是有些公司却要求有这个约束,不然每次插入数据或者修改数据的时候有可能插入一个没关系的数据。但是外键约束对于查询速度没有什么影响。就算感觉快那也是索引的原因。
其实创建外键约束,是有成本的,需要消耗系统资源的,对于大并发数的SQL操作,有可能会不适合,保留大型网站的重要数据库,可能因外键约束而岛主系统开销变大从而变慢。所以很多时候开发者通过再插入数据之前在应用层进行一个限制,比如只能插入某些值,而不是随便插入等。
强制
不得使用外键与级联,一切外键概念必须在应用层解决。其实外键约束也是可以设置等级的
一般不设置的化,默认就是Resrict 方式. 不过一般在最好也是最常用的是:ON UPDATE CASCADE ON DELETE SET NULL
现在进行演示:
CREATE TABLE table_a1( a_id INT PRIMARY KEY, a_name VARCHAR(2) ); CREATE TABLE table_b1( a_id INT, b_id INT, b_name VARCHAR(10), -- 这个地方可以如果修改表会同步,但是删除会自动设置为空 CONSTRAINT b_a1 FOREIGN KEY (a_id) REFERENCES table_a1(a_id) ON UPDATE CASCADE ON DELETE SET NULL ); -- 插入数据 INSERT INTO table_a1 VALUES (1,'语文'); INSERT INTO table_a1 VALUES (2,'数学'); INSERT INTO table_a1 VALUES (3,'英语'); INSERT INTO test.table_b1 VALUES ('1', '1', '张三'); INSERT INTO test.table_b1 VALUES ('2', '1', '张三'); INSERT INTO test.table_b1 VALUES ('2', '2', '李四');
现在执行:
UPDATE table_a1 SET a_id='4' WHERE a_id='1'
然后查看从表:
然后再删除主表中某个值
DELETE FROM table_a1 WHERE a_id='2'
CHECK 约束的作用是检查某个字段的值是否满足某个要求,一般是值的范围(不一定是数字)。
其实在5.7版本及以前虽然可以使用check约束,但是没有效果,后面再8.0版本之后可以使用了。
CREATE TABLE 主表名(
字段1 字段类型 ,
字段2 字段类型 CHECK (值的范围),
.........
)
演示
CREATE TABLE test(
a INT CHECK (a>100),
)
--
INSERT INTO test VALUES (1000);
但是插入一个小于100的数试试:
INSERT INTO test VALUES (10);
当然也可以限制某些字符串:
CREATE TABLE test1(
sex CHAR CHECK ( sex IN ('男' OR '女'))
)
就不在低版本演示了,毕竟低版本演示也没有什么效果。
给某个字段或某列指定一个默认值,一旦插入是无法显示的值,就会赋值默认值。
CREATE TABLE 主表名(
字段1 字段类型 DEFAULT 默认值,
字段2 字段类型 ,
.........
)
演示
CREATE TABLE test(
a INT DEFAULT 10,
b VARCHAR(10)
)
INSERT INTO test VALUES (1,'a');
INSERT INTO test (b) VALUES ('b');
INSERT INTO test VALUES (NULL,'b');
很多时候在创建白哦的时候会加入 NOT NULL DEFAULT ‘’ 或者 DEFAULT 0。为了不让表中出NULL,毕竟NULL 会影响比较,有时候会影响运算,而且效率不高,影响索引效果。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。