表的约束
空属性
- 空属性有两个值,分别是null和not null。
- 数据库默认字段基本都是允许为空的,但在实际开发中我们要尽可能保证字段不为空,因为空值无法参与运算
让某个字段不允许为空,在创建表的时候就可以给对应字段设置not null属性。
例如:创建一个班级表,表当中包含班级名和该班级所在的教室,如果插入数据时不想让这两个字段为空,就可以在创建表时给这两个字段设置not null属性
mysql> create table if not exists my_class (
-> class_name varchar(20) not null ,
-> class_room varchar(20) not null ,
-> other varchar(20)
-> );
Query OK, 0 rows affected (0.01 sec)
创建表完毕后查看表结构,可以看到这两个字段是不允许为空的
向表中插入数据时只有这两个字段都不为空时才能插入成功,否则将会插入失败
默认值
mysql> create table if not exists t13(
-> name varchar (20) not null ,
-> age tinyint unsigned default 18 ,
-> gender char (1) default '男'
-> );
mysql> insert into t13 (name ,age ,gender ) values ('张三' , 19 ,'女') ;
Query OK, 1 row affected (0.00 sec)
mysql> select * from t13;
+--------+------+--------+
| name | age | gender |
+--------+------+--------+
| 张三 | 19 | 女 |
+--------+------+--------+
1 row in set (0.00 sec)
mysql> insert into t13 (name ) values ('李四') ;
Query OK, 1 row affected (0.01 sec)
向表中插入数据时,如果不指明用户的年龄或性别,那么就会使用对应的默认值,如果指明了就会使用用户指定的值
同时设置not null和default
- 一旦给某一字段设置了默认值,那么该字段将不会出现空值,因为就算插入数据时没有指明该字段的值,也会使用该字段的默认值进行填充。
- 而给某一字段设置not null属性的目的是约束该字段不能为空,因此一个字段设置了default属性后,再设置not null属性就没有意义了。
比如创建一个id表,表当中包括姓名和id,将id同时设置default和not null属性
在向表中插入数据时可以不指明id进行插入,此时会使用id的默认值
mysql> create table id_table(
-> name varchar(20) ,
-> id int not null default 1
-> );
Query OK, 0 rows affected (0.01 sec)
mysql> desc id_table;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| name | varchar(20) | YES | | NULL | |
| id | int(11) | NO | | 1 | |
+-------+-------------+------+-----+---------+-------+
2 rows in set (0.00 sec)
mysql> insert id_table (name) values ('张三') ;
Query OK, 1 row affected (0.01 sec)
mysql> select * from id_table;
+--------+----+
| name | id |
+--------+----+
| 张三 | 1 |
+--------+----+
1 row in set (0.00 sec)
列描述
列描述是在创建表的时候用来对各个字段进行描述的,列描述会根据表创建语句保存,一般是用来给程序员或DBA了解表的相关信息的,相当于一种注释
例如:
创建一个用户表,表当中包含用户名、用户的年龄和用户的性别,在每一个字段后面添加上对应的列描述。
mysql> create table if not exists user_table (
-> name varchar(20) not null comment '用户名' ,
-> age tinyint unsigned default 18 comment '用户的年龄',
-> sex char(1) default '男' comment '性别'
-> );
Query OK, 0 rows affected (0.02 sec)
查看到创建表时的相关细节,包括列描述
mysql> show create table user_table \G
*************************** 1. row ***************************
Table: user_table
Create Table: CREATE TABLE `user_table` (
`name` varchar(20) NOT NULL COMMENT '用户名',
`age` tinyint(3) unsigned DEFAULT '18' COMMENT '用户的年龄',
`sex` char(1) DEFAULT '男' COMMENT '性别'
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
zerofill
数值类型后面的圆括号中的数字,代表的是显示宽度,对应数值类型设置zerofill属性后,如果数据的宽度小于设定的宽度则自动填充0。
例如:
创建一个表,表当中包含a和b两列整型数据,将它们的显示宽度都设置成5,此时没有设置zerofill属性。
向表中插入一条数据,指明a和b的值均为1,没有给a和b字段设置zerofill属性,因此查看表中数据时显示出来的都是1,并没有显示宽度的概念
mysql> create table test(
-> a int(5) unsigned ,
-> b int(5) unsigned
-> );
Query OK, 0 rows affected (0.01 sec)
mysql> insert into test values(1,1) ;
Query OK, 1 row affected (0.00 sec)
mysql> select * from test ;
+------+------+
| a | b |
+------+------+
| 1 | 1 |
+------+------+
1 row in set (0.00 sec)
修改表结构,给a列添加上zerofill属性
mysql> alter table test modify a int(5) unsigned zerofill ;
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> select * from test;
+-------+------+
| a | b |
+-------+------+
| 00001 | 1 |
+-------+------+
1 row in set (0.00 sec)
a列数据的显示宽度为5,因此查看表中数据可以看到a列数据中宽度不足5位的数据都自动在前面填充0了
zerofill属性的作用就是让数据以特定的方式进行显示而已,数据底层的储存方式并没有发生变化,通过hex函数可以看到a列中显示的00001在底层实际储存的还是1
mysql> select a , hex(a) from test;
+-------+--------+
| a | hex(a) |
+-------+--------+
| 00001 | 1 |
+-------+--------+
1 row in set (0.00 sec)
主键
向表当中插入一条条数据后,为了方便后续进行查找,我们可以选择其中的某一字段作为键值key,当需要查找数据时就根据这个键值key来查找对应的数据。
主键用来唯一的约束该字段里面的数据,表当中每条数据的主键不能重复也不能为空,并且一张表里面只能有一个主键,此外,主键所在的列通常是整数类型。
例如:
创建一个学生表,表当中包含学生的学号和姓名,由于学生的学号是不会重复的,因此可以将其设置成主键
mysql> create table stu(
-> id int unsigned primary key comment '学号' ,
-> name varchar (20) not null comment'姓名'
-> );
Query OK, 0 rows affected (0.02 sec)
id对应的Key列出现了PRI,表示将学号设置成这张表的主键。此外,虽然在创建表的时候没有给学号设置not null属性,但由于主键本身就是不能为空的,因此id默认也就不能为空了
mysql> desc stu;
+-------+------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+------------------+------+-----+---------+-------+
| id | int(10) unsigned | NO | PRI | NULL | |
| name | varchar(20) | NO | | NULL | |
+-------+------------------+------+-----+---------+-------+
2 rows in set (0.01 sec)
主键约束就是,插入表中的数据的主键字段不能重复,如果插入数据的主键与表中已有数据的主键重复,这时就会因为主键冲突而插入失败
删除指定表的主键,因为一个表只有一个主键,因此删除主键时只用指明要删除哪张表的主键即可
alter table 表名 drop primary key
mysql> alter table stu drop primary key ;
Query OK, 1 row affected (0.03 sec)
Records: 1 Duplicates: 0 Warnings: 0
mysql> desc stu;
+-------+------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+------------------+------+-----+---------+-------+
| id | int(10) unsigned | NO | | NULL | |
| name | varchar(20) | NO | | NULL | |
+-------+------------------+------+-----+---------+-------+
2 rows in set (0.00 sec)
对于已经创建好了的表,可以给指定列设置成主键
alter table 表名 add primary key(列名)
mysql> alter table stu add primary key(id) ;
Query OK, 0 rows affected (0.04 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> desc stu;
+-------+------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+------------------+------+-----+---------+-------+
| id | int(10) unsigned | NO | PRI | NULL | |
| name | varchar(20) | NO | | NULL | |
+-------+------------------+------+-----+---------+-------+
2 rows in set (0.00 sec)
注意:只有列当中的值不为空并且不重复的列才能被设置成主键。例如:重新将学号设置成学生表的主键后再查看表结构,可以看到id对应的Key列的PRI又回来了
利用主键找到唯一的id,对name进行修改
mysql> select * from stu;
+----+--------+
| id | name |
+----+--------+
| 1 | 张三 |
| 2 | 林季 |
| 3 | 九品 |
+----+--------+
3 rows in set (0.00 sec)
mysql> update stu set name='妖捕' where id=2;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select * from stu;
+----+--------+
| id | name |
+----+--------+
| 1 | 张三 |
| 2 | 妖捕 |
| 3 | 九品 |
+----+--------+
3 rows in set (0.00 sec)
复合主键
一张表中最多只能有一个主键,不意味着一个表中的主键,只能添加给一列! 一个主键可以被添加到一列,或者多列上(复合主键)
- 一张表里面只能有一个主键,但一个主键可以由多个字段来承担,这种主键叫做复合主键。
- 复合主键用来唯一约束多个字段里面的数据,表当中每条数据的这多个字段不能同时重复也不能为空。
创建一个进程表,表当中包含进程的IP地址、端口号和进程的相关信息,并将IP地址和端口号组合起来形成一个复合主键
mysql> create table process_table (
-> ip varchar (30) comment 'IP地址',
-> port int unsigned comment 'port端口号',
-> info varchar (64) comment '相关信息',
-> primary key(ip, port)
-> );
Query OK, 0 rows affected (0.01 sec)
mysql> desc process_table ;
+-------+------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+------------------+------+-----+---------+-------+
| ip | varchar(30) | NO | PRI | NULL | |
| port | int(10) unsigned | NO | PRI | NULL | |
| info | varchar(64) | YES | | NULL | |
+-------+------------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
查看表结构,可以看到ip和port的Key列都有PRI标志,并且它们都是不允许为空的。
向进程表中插入数据时,只有插入进程的IP和端口均出现冲突时才会产生主键冲突,否则就允许插入
删除指定表的复合主键
alter table 表名 drop primary key
mysql> select * from process_table;
+-----------+------+--------------+
| ip | port | info |
+-----------+------+--------------+
| 127.0.0.1 | 8081 | 测试进程 |
| 127.0.0.1 | 8082 | 测试进程 |
| 127.0.0.2 | 8081 | 测试进程 |
+-----------+------+--------------+
3 rows in set (0.00 sec)
mysql> alter table process_table drop primary key ;
Query OK, 3 rows affected (0.04 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> desc process_table ;
+-------+------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+------------------+------+-----+---------+-------+
| ip | varchar(30) | NO | | NULL | |
| port | int(10) unsigned | NO | | NULL | |
| info | varchar(64) | YES | | NULL | |
+-------+------------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
已经创建好了的表,可以用多个列形成复合主键
alter table 表名 add primary key(多个列名)
mysql> alter table process_table add primary key (ip,port) ;
Query OK, 0 rows affected (0.04 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> desc process_table ;
+-------+------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+------------------+------+-----+---------+-------+
| ip | varchar(30) | NO | PRI | NULL | |
| port | int(10) unsigned | NO | PRI | NULL | |
| info | varchar(64) | YES | | NULL | |
+-------+------------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
自增长
- 设置了自增长属性的字段,插入数据时如果不给该字段值,那么系统会自动找出当前字段当中已有的最大值,将最大值进行加一后的值插入该字段。
- 任何一个字段要做自增长,前提是其本身必须是一个索引(Key一栏有值),并且自增长字段必须是数值类型,一张表最多只能有一个自增长字段。
- 自增长通常和主键搭配使用,作为逻辑主键。一般而言,建议将主键设计成与当前业务无关的字段,避免因为业务逻辑的调整而需要修改主键。
例如:创建一个表,表当中包含id和name,将id同时设置成主键和自增长字段
mysql> create table auto_increment_table(
id int unsigned primary key auto_increment,
name varchar (20) );
Query OK, 0 rows affected (0.01 sec)
mysql> desc auto_increment_table ;
+-------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| name | varchar(20) | YES | | NULL | |
+-------+------------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)
向表中插入第一条数据时如果没有指明自增长字段的值,那么自增长字段的值默认将会从1开始
mysql> insert into auto_increment_table (name) values('张三') ;
Query OK, 1 row affected (0.00 sec)
mysql> select * from auto_increment_table ;
+----+--------+
| id | name |
+----+--------+
| 1 | 张三 |
+----+--------+
1 row in set (0.00 sec)
后续向表中插入数据时如果也不指明自增长字段的值,那么自增长字段的值就会依次递增
mysql> insert into auto_increment_table (name) values('李四') ;
Query OK, 1 row affected (0.01 sec)
mysql> insert into auto_increment_table (name) values('王五') ;
Query OK, 1 row affected (0.00 sec)
mysql> insert into auto_increment_table (name) values('赵六') ;
Query OK, 1 row affected (0.00 sec)
mysql> select * from auto_increment_table ;
+----+--------+
| id | name |
+----+--------+
| 1 | 张三 |
| 2 | 李四 |
| 3 | 王五 |
| 4 | 赵六 |
+----+--------+
4 rows in set (0.00 sec)
插入数据的时候也可以指明自增长字段的值,此时将会使用该值进行插入,但注意指明的值不能和表中已有的id值重复
mysql> insert into auto_increment_table (id,name) values(12,'神仙') ;
Query OK, 1 row affected (0.00 sec)
mysql> select * from auto_increment_table ;
+----+--------+
| id | name |
+----+--------+
| 1 | 张三 |
| 2 | 李四 |
| 3 | 王五 |
| 4 | 赵六 |
| 12 | 神仙 |
+----+--------+
5 rows in set (0.00 sec)
此后向表中插入数据时如果又不指明自增长字段的值,那么自增长字段的值将会从id列中找出最大值,将最大值加一后得到的值作为自增长字段的值进行插入
自增长原因:根据AUTO_INCREMENT=500 来增长
例如:建立一个表,设置自增长为500
mysql> create table ff(
-> id int unsigned primary key auto_increment ,
-> name varchar(20) not null
-> )auto_increment=500;
Query OK, 0 rows affected (0.01 sec)
mysql> insert into ff (name) values('卫宓');
Query OK, 1 row affected (0.00 sec)
mysql> select * from ff ;
+-----+--------+
| id | name |
+-----+--------+
| 500 | 卫宓 |
+-----+--------+
1 row in set (0.00 sec)
mysql> show create table ff \G
*************************** 1. row ***************************
Table: ff
Create Table: CREATE TABLE `ff` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(20) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=501 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
select last_insert_id() , 返回最近一次 INSERT操作生成的自增(AUTO_INCREMENT)ID
mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
| 500 |
+------------------+
1 row in set (0.00 sec)
唯一键
- 一张表中往往有很多字段需要唯一性,但一张表中只能有一个主键,而唯一键就可以解决表中有多个字段需要唯一性约束的问题。
- 唯一键和主键都能保证字段中数据的唯一性,但唯一键允许字段为空,并且可以多个字段为空,空字段不做唯一性比较。
- 注意:不是主键具有唯一性,而是某个具有唯一性的字段被选择成为了主键,而那些不是主键但是同样需要唯一性约束的字段就应该设置成唯一键
mysql> create table student(
id int unsigned primary key auto_increment comment '学生的.号 ',
name varchar (20) not null comment '学生的姓名' ,
tel int unsigned unique comment '学生的.话号码 '
);
Query OK, 0 rows affected (0.01 sec)
向表中插入数据时,如果插入数据中的电话号码与表中已有数据的电话号码出现重复,那么就会因为唯一键冲突而插入失败
mysql> insert into student (name,tel) values ('张三',123456);
Query OK, 1 row affected (0.00 sec)
mysql> insert into student (name,tel) values ('云缺',123456);
ERROR 1062 (23000): Duplicate entry '123456' for key 'tel'
此外,向表中插入的数据可以不指明唯一键字段的值,此时该字段默认为空,不做唯一性比较。
删除delete
mysql> select * from student;
+----+--------+--------+
| id | name | tel |
+----+--------+--------+
| 1 | 张三 | 123456 |
| 3 | 云缺 | NULL |
+----+--------+--------+
2 rows in set (0.00 sec)
mysql> delete from student where tel is null;
Query OK, 1 row affected (0.00 sec)
mysql> select * from student;
+----+--------+--------+
| id | name | tel |
+----+--------+--------+
| 1 | 张三 | 123456 |
+----+--------+--------+
1 row in set (0.00 sec)
外键
- 外键用来定义主表和从表之间的关系,外键约束主要定义在从表上,主表必须有主键约束或唯一键约束。
- 外键定义后,要求插入外键列的数据必须在主表对应的列存在或为null
创建一个班级表作为主表,表当中包含班级的id和班级名,并将班级id设置为主键
mysql> create table class_table(
-> class_id int unsigned primary key comment '班级ID' ,
-> name varchar(20) not null comment '班级名'
-> );
Query OK, 0 rows affected (0.01 sec)
再创建一个学生表作为从表,表当中包含学生的id、姓名以及学生所在班级对应的id,并将学生表中的班级id列设置成外键,关联到班级表中的班级id列
mysql> create table student_table(
-> stu_id int unsigned primary key comment '学生ID' ,
-> name varchar(20) not null comment '学生姓名',
-> class_id int unsigned comment '学生所在班级对应的id ' ,
-> foreign key (class_id) references class_table(class_id)
-> );
出现MUL标志,class_id设置外键成功
向班级表中插入两条数据
mysql> insert into class_table values (1,'计科1班');
Query OK, 1 row affected (0.00 sec)
mysql> insert into class_table values (2,'计科2班');
Query OK, 1 row affected (0.00 sec)
mysql> select * from class_table ;
+----------+------------+
| class_id | name |
+----------+------------+
| 1 | 计科1班 |
| 2 | 计科2班 |
+----------+------------+
2 rows in set (0.00 sec)
向学生表中插入数据
mysql> insert into student_table values(1,'张三',1);
Query OK, 1 row affected (0.01 sec)
mysql> insert into student_table values(2,'江寒',2);
Query OK, 1 row affected (0.00 sec)
mysql> insert into student_table values(3,'寒清薇',null);
Query OK, 1 row affected (0.00 sec)
mysql> select * from student_table ;
+--------+-----------+----------+
| stu_id | name | class_id |
+--------+-----------+----------+
| 1 | 张三 | 1 |
| 2 | 江寒 | 2 |
| 3 | 寒清薇 | NULL |
+--------+-----------+----------+
3 rows in set (0.00 sec)
外键约束:插入student_table的数据对应的class_id是3,相当于插入student_table的数据对应的class_id不存在,此时插入失败
此时向class_table中插入class_id为3的班级信息,然后再向student_table中插入数据,这时就允许插入了
mysql> select * from class_table ;
+----------+------------+
| class_id | name |
+----------+------------+
| 1 | 计科1班 |
| 2 | 计科2班 |
+----------+------------+
2 rows in set (0.00 sec)
mysql> insert into class_table values(3,'计科3班');
Query OK, 1 row affected (0.01 sec)
mysql> insert into student_table values(4,'韩宇',3);
Query OK, 1 row affected (0.00 sec)
mysql> select * from student_table ;
+--------+-----------+----------+
| stu_id | name | class_id |
+--------+-----------+----------+
| 1 | 张三 | 1 |
| 2 | 江寒 | 2 |
| 3 | 寒清薇 | NULL |
| 4 | 韩宇 | 3 |
+--------+-----------+----------+
4 rows in set (0.00 sec)
创建class_table和student_table后就算不设置外键,在语义上其实也已经有了外键,但这样我们没办法保证后续插入学生表的数据中的班级id的正确性。
而我们给学生表中的class_id设置成外键后,外键约束就能保证只有班级id在班级表中存在的数据才能插入学生表,否则就会插入失败。
实际建立外键的本质就是把相关性交给MySQL去审核了,提前告诉MySQL表之间的约束关系,当用户插入不符合业务逻辑的数据时,MySQL就不允许你进行插入。