原文: http://blog.gqylpy.com/gqy/249
"
目录
约束条件与数据类型的宽度一样,都是可选参数
作用:用于保证数据的完整性和一致性
主要分为:
- primary key (pk) # 标识该字段为该表的主键,能够唯一的标识记录
- foreign key (pk) # 标识该字段为该表的外键
- not null # 标识该字段不能为空
- unique key (uk) # 标识该字段的值是唯一的
- auto_increment # 标识该字段的值自动增长(整数类型,而且为主键)
- default # 为该字段设置默认值
- unsigned # 无符号
- zerofill # 使用0填充
说明:
- 是否允许为空,默认为null,设置not null,使字段不允许为空,必须赋值.
- 字段是否有默认值,缺省的默认值是null,如果插入记录时不给字段赋值,则此字段使用默认值null.
(设置枚举的默认值,且不为空:sex enum('boy', 'girl') not null default 'boy')
(设置年龄必须为正值(无符号)且不为空,默认为20:age int unsigned not null default 20)
not null
设置not null,插入值时不能为空.
-
# 创建测试表
-
mysql>
create
table
test(
id
int
not
null);
-
Query OK, 0 rows affected (0.08 sec)
-
-
# 此时查看表结构,Null的标识为NO,即不允许为空
-
mysql> desc test;
-
+
-------+---------+------+-----+---------+-------+
-
| Field | Type | Null | Key | Default | Extra |
-
+
-------+---------+------+-----+---------+-------+
-
| id | int(11) | NO | | NULL | |
-
+
-------+---------+------+-----+---------+-------+
-
1 row in
set (
0.00 sec)
-
-
# 插入一个空字符串是可以的(本人是在Mac系统上实测,这一步骤可能与Windows或Linux有差异)
-
mysql>
insert
into
test
values();
-
Query OK, 1 row affected, 1 warning (0.03 sec)
-
-
# 插入null时,直接报错
-
mysql>
insert
into
test
values(
null);
-
ERROR 1048 (23000): Column 'id' cannot be null
-
-
# 此时查询记录
-
# 可见:int类型设置not null后,插入空字符串会替换成0
-
mysql>
select *
from
test;
-
+
----+
-
| id |
-
+
----+
-
| 0 |
-
+
----+
-
1 row in
set (
0.00 sec)
default
设置一个字段有默认值后,则无论这个字段是null还是not null,都可以插入空,插入空默认填入default指定的默认值.
-
# 第一种情况:default
-
mysql>
create
table
test(
id
int
default
1);
-
Query OK, 0 rows affected (0.07 sec)
-
-
mysql> desc test;
-
+
-------+---------+------+-----+---------+-------+
-
| Field | Type | Null | Key | Default | Extra |
-
+
-------+---------+------+-----+---------+-------+
-
| id | int(11) | YES | | 1 | |
-
+
-------+---------+------+-----+---------+-------+
-
1 row in
set (
0.00 sec)
-
-
mysql>
insert
into
test
values();
-
Query OK, 1 row affected (0.04 sec)
-
-
mysql>
select *
from
test;
-
+
------+
-
| id |
-
+
------+
-
| 1 |
-
+
------+
-
1 row in
set (
0.00 sec)
-
-
-
# 第二种情况:
not
null +
default
-
mysql>
create
table
test(
id
int
not
null
default
2);
-
Query OK, 0 rows affected (0.05 sec)
-
-
mysql> desc test;
-
+
-------+---------+------+-----+---------+-------+
-
| Field | Type | Null | Key | Default | Extra |
-
+
-------+---------+------+-----+---------+-------+
-
| id | int(11) | NO | | 2 | |
-
+
-------+---------+------+-----+---------+-------+
-
1 row in
set (
0.00 sec)
-
-
mysql>
insert
into
test
values();
-
Query OK, 1 row affected (0.04 sec)
-
-
mysql>
select *
from
test;
-
+
----+
-
| id |
-
+
----+
-
| 2 |
-
+
----+
-
1 row in
set (
0.00 sec)
unique
中文翻译:不同的。在MySQL中称为单列唯一
单列唯一
-
# 创建unique方式1:
-
# 指定name唯一
-
mysql>
create
table
test(
-
->
id
int,
-
->
name
char(
3)
unique
-
-> );
-
Query OK, 0 rows affected (0.17 sec)
-
-
# 插入相同的name,直接报错
-
mysql>
insert
into
test
values(
1,
'zyk'), (
2,
'zyk');
-
ERROR 1062 (23000): Duplicate entry 'zyk' for key 'name'
-
-
# 此时正常
-
mysql>
insert
into
test
values(
1,
'zyk'), (
2,
'xhh');
-
Query OK, 2 rows affected (0.01 sec)
-
Records: 2 Duplicates: 0 Warnings: 0
-
-
-
# 创建unique方式2:
-
mysql>
create
table
test(
-
->
id
int,
-
->
name
char(
3),
-
->
unique(
id),
-
->
unique(
name)
-
-> );
-
Query OK, 0 rows affected (0.09 sec)
-
-
# 可见:Key标识为UNI
-
mysql> desc test;
-
+
-------+---------+------+-----+---------+-------+
-
| Field | Type | Null | Key | Default | Extra |
-
+
-------+---------+------+-----+---------+-------+
-
| id | int(11) | YES | UNI | NULL | |
-
| name | char(3) | YES | UNI | NULL | |
-
+
-------+---------+------+-----+---------+-------+
-
2 rows in
set (
0.00 sec)
联合唯一
即指定表中两条或以上的记录,只有当这些被指定的记录都相同时才符合联合唯一,否则不会被限制.
-
mysql>
create
table
test(
-
-> ip
char(
15),
-
-> port
int,
-
->
unique(ip, port) # 联合唯一
-
-> );
-
Query OK, 0 rows affected (0.08 sec)
-
-
# 此时查看表结构,Key的标识为MUL
-
mysql> desc test;
-
+
-------+----------+------+-----+---------+-------+
-
| Field | Type | Null | Key | Default | Extra |
-
+
-------+----------+------+-----+---------+-------+
-
| ip | char(15) | YES | MUL | NULL | |
-
| port | int(11) | YES | | NULL | |
-
+
-------+----------+------+-----+---------+-------+
-
2 rows in
set (
0.00 sec)
-
-
# 插入两条不完全相同的数据
-
mysql>
insert
into
test
values
-
-> (
'192.168.1.1',
3306),
-
-> (
'192.168.1.2',
3306);
-
Query OK, 2 rows affected (0.03 sec) # 成功插入
-
Records: 2 Duplicates: 0 Warnings: 0
-
-
# 再插入一条存在的数据,被阻止
-
mysql>
insert
into
test
values(
'192.168.1.1',
3306);
-
ERROR 1062 (23000): Duplicate entry '192.168.1.1 -3306' for key 'ip'
primary key
一张表中可以:单列做主键,多列做主键(复合主键)
约束:字段的值不为空且值唯一,等价于not null unique
MySQL的存储引擎默认是InnoDB,对于InnoDB存储引擎来说,一张表必须有一个主键.
单列主键
-
mysql>
create
table
test(
-
->
id
int primary
key, # 单列主键
-
->
name
char(
3)
-
-> );
-
Query OK, 0 rows affected (0.05 sec)
-
-
# 此时查看表结构,Key的标识为PRI
-
mysql> desc test;
-
+
-------+---------+------+-----+---------+-------+
-
| Field | Type | Null | Key | Default | Extra |
-
+
-------+---------+------+-----+---------+-------+
-
| id | int(11) | NO | PRI | NULL | |
-
| name | char(3) | YES | | NULL | |
-
+
-------+---------+------+-----+---------+-------+
-
2 rows in
set (
0.00 sec)
-
-
# 插入两条
id不同的数据
-
mysql>
insert
into
test
values
-
-> (
1,
'zyk'),
-
-> (
2,
'xhh');
-
Query OK, 2 rows affected (0.00 sec)
-
Records: 2 Duplicates: 0 Warnings: 0
-
-
# id已存在,阻止插入
-
mysql>
insert
into
test
value(
2,
'jein');
-
ERROR 1062 (23000): Duplicate entry '2' for key 'PRIMARY'
复合主键
复合主键与联合唯一同理:
指定表中两条或以上的记录,只有当这些被指定的记录都相同时才符合复合主键的限制,否则不会被限制.
-
mysql>
create
table
test(
-
-> ip
char(
15),
-
-> port
int,
-
-> primary
key(ip, port) # 复合主键
-
-> );
-
Query OK, 0 rows affected (0.06 sec)
-
-
mysql>
insert
into
test
values
-
-> (
'192.168.1.1',
3306),
-
-> (
'192.168.1.2',
3306);
-
Query OK, 2 rows affected (0.00 sec)
-
Records: 2 Duplicates: 0 Warnings: 0
-
-
mysql>
insert
into
test
values(
'192.168.1.1',
3306);
-
ERROR 1062 (23000): Duplicate entry '192.168.1.1 -3306' for key 'PRIMARY'
-
-
mysql> desc test;
-
+
-------+----------+------+-----+---------+-------+
-
| Field | Type | Null | Key | Default | Extra |
-
+
-------+----------+------+-----+---------+-------+
-
| ip | char(15) | NO | PRI | NULL | |
-
| port | int(11) | NO | PRI | NULL | |
-
+
-------+----------+------+-----+---------+-------+
-
2 rows in
set (
0.01 sec)
auto_increment
被约束的字段自动增长,且必须同时被key约束.
不指定id,则自动增长:
-
mysql>
create
table
test(
-
->
id
int primary
key auto_increment, # 约束字段自动增长
-
->
name
varchar(
20),
-
-> sex enum(
'boy',
'girl')
default
'boy'
-
-> );
-
Query OK, 0 rows affected (0.05 sec)
-
-
# 表结构显示Extra的标识为auto_increment,即自动增长
-
mysql> desc test;
-
+
-------+--------------------+------+-----+---------+----------------+
-
| Field | Type | Null | Key | Default | Extra |
-
+
-------+--------------------+------+-----+---------+----------------+
-
| id | int(11) | NO | PRI | NULL | auto_increment |
-
| name | varchar(20) | YES | | NULL | |
-
| sex | enum('boy','girl') | YES | | boy | |
-
+
-------+--------------------+------+-----+---------+----------------+
-
3 rows in
set (
0.00 sec)
-
-
# 插入两条记录
-
mysql>
insert
into
test(
name)
values (
'zyk'), (
'xhh');
-
Query OK, 2 rows affected (0.00 sec)
-
Records: 2 Duplicates: 0 Warnings: 0
-
-
# 可见,id确为自动增长
-
mysql>
select *
from
test;
-
+
----+------+------+
-
| id | name | sex |
-
+
----+------+------+
-
| 1 | zyk | boy |
-
| 2 | xhh | boy |
-
+
----+------+------+
-
2 rows in
set (
0.00 sec)
指定id:
-
# 指定id插入两条数据
-
mysql>
insert
into
test
values
-
-> (
4,
'join',
'girl'),
-
-> (
7,
'lay',
'girl');
-
Query OK, 2 rows affected (0.01 sec)
-
Records: 2 Duplicates: 0 Warnings: 0
-
-
mysql>
select *
from
test;
-
+
----+------+------+
-
| id | name | sex |
-
+
----+------+------+
-
| 1 | zyk | boy |
-
| 2 | xhh | boy |
-
| 4 | join | girl |
-
| 7 | lay | girl |
-
+
----+------+------+
-
4 rows in
set (
0.00 sec)
-
-
# 再次插入一条不指定
id的记录,会随着值最大的
id+
1
-
mysql>
insert
into
test(
name)
values (
'great white');
-
Query OK, 1 row affected (0.00 sec)
-
-
mysql>
select *
from
test;
-
+
----+-------------+------+
-
| id | name | sex |
-
+
----+-------------+------+
-
| 1 | zyk | boy |
-
| 2 | xhh | boy |
-
| 4 | join | girl |
-
| 7 | lay | girl |
-
| 8 | great white | boy |
-
+
----+-------------+------+
-
5 rows in
set (
0.00 sec)
-
-
#
-
mysql>
insert
into
test(
id,
name)
values (
3,
'aaa');
-
Query OK, 1 row affected (0.04 sec)
-
-
mysql>
select *
from
test;
-
+
----+-------------+------+
-
| id | name | sex |
-
+
----+-------------+------+
-
| 1 | zyk | boy |
-
| 2 | xhh | boy |
-
| 3 | aaa | boy |
-
| 4 | join | girl |
-
| 7 | lay | girl |
-
| 8 | great white | boy |
-
+
----+-------------+------+
-
6 rows in
set (
0.00 sec)
对于自增的字段,在用delete删除后,再插入值,改字段仍按照删除前的位置继续增长.
应使用truncate清空表,trunacte是直接清空表,再删除数据量庞大的表时非常有用.
-
#
delete删除表记录
-
mysql>
delete
from
test;
-
Query OK, 8 rows affected (0.00 sec)
-
-
mysql>
insert
into
test(
name)
values(
'zyk');
-
Query OK, 1 row affected (0.04 sec)
-
-
# 可见:
delete删除表记录后,再插入值,仍然是按照删除前的位置继续增长
-
mysql>
select *
from
test;
-
+
----+------+------+
-
| id | name | sex |
-
+
----+------+------+
-
| 9 | zyk | boy |
-
+
----+------+------+
-
1 row in
set (
0.00 sec)
-
-
#
truncate清空表
-
mysql>
truncate
test;
-
Query OK, 0 rows affected (0.00 sec)
-
-
mysql>
insert
into
test(
name)
values(
'zyk');
-
Query OK, 1 row affected (0.00 sec)
-
-
# 此时id重新开始自增
-
mysql>
select *
from
test;
-
+
----+------+------+
-
| id | name | sex |
-
+
----+------+------+
-
| 1 | zyk | boy |
-
+
----+------+------+
-
1 row in
set (
0.00 sec)
清空表分区 delete 与 truncate 的区别:
- delete from tb # 有自增id时,删除后新增的数据的id仍然是按照未删除之前最大的id+1计算的
- truncate table tb # 直接清空表,数据量大时非常适用,删除速度比delete快,且id直接从0开始
步长与偏移量
auto_increment_increment:步长,默认为1
auto_increment_offset:起始的偏移量,默认为1
注意:如果偏移量大于步长,则偏移量的会被重设为1
-
# 查看步长与偏移量
-
mysql>
show
variables
like
'auto_i%';
-
+
--------------------------+-------+
-
| Variable_name | Value |
-
+
--------------------------+-------+
-
| auto_increment_increment | 1 | # 步长
-
| auto_increment_offset | 1 | # 偏移量
-
+
--------------------------+-------+
-
2 rows in
set (
0.00 sec)
-
-
#
session为会话设置,只在本次链接中有效
-
mysql>
set
session auto_increment_increment =
5;
-
Query OK, 0 rows affected (0.00 sec)
-
-
# global全局(设置后需重新连接,才会生效)
-
mysql>
set
global auto_increment_increment =
5;
-
Query OK, 0 rows affected (0.00 sec)
-
-
mysql>
set
global auto_increment_offset =
3;
-
Query OK, 0 rows affected (0.00 sec)
-
-
# 重新连接后查看
-
mysql>
show
variables
like
'auto_i%';
-
+
--------------------------+-------+
-
| Variable_name | Value |
-
+
--------------------------+-------+
-
| auto_increment_increment | 5 |
-
| auto_increment_offset | 3 |
-
+
--------------------------+-------+
-
2 rows in
set (
0.00 sec)
-
-
# 先清空表
-
mysql>
truncate
table
test;
-
Query OK, 0 rows affected (0.04 sec)
-
-
# 插入一条记录,可见:id是从3开始计算的
-
mysql>
insert
into
test(
name)
values (
'zyk');
-
Query OK, 1 row affected (0.04 sec)
-
-
mysql>
select *
from
test;
-
+
----+------+------+
-
| id | name | sex |
-
+
----+------+------+
-
| 3 | zyk | boy |
-
+
----+------+------+
-
1 row in
set (
0.01 sec)
-
-
# 再插入几条数据,可见:
id每次自增
5
-
mysql>
insert
into
test(
name)
values (
'zyk1'), (
'zyk2'), (
'zyk3');
-
Query OK, 3 rows affected (0.00 sec)
-
Records: 3 Duplicates: 0 Warnings: 0
-
-
mysql>
select *
from
test;
-
+
----+------+------+
-
| id | name | sex |
-
+
----+------+------+
-
| 3 | zyk | boy |
-
| 8 | zyk1 | boy |
-
| 13 | zyk2 | boy |
-
| 18 | zyk3 | boy |
-
+
----+------+------+
-
4 rows in
set (
0.00 sec)
foreign key
一 快速理解foreign key
之前创建表的时候都是在一张表中添加记录,比如如下表:
![在这里插入图片描述](http://blog.gqylpy.com/media/ai/2019-03/c5c57780-f4f5-4916-8031-59471269e7d6.png)
公司有3个部门,但是有1个亿的员工,那意味着部门这个字段需要重复存储,部门名字越长,越浪费。
这个时候,
解决方法:
我们完全可以定义一个部门表
然后让员工信息表关联该表,如何关联,即foreign key
我们可以将上表改为如下结构:
![在这里插入图片描述](http://blog.gqylpy.com/media/ai/2019-03/fdc13268-a918-46ed-9458-6cd43d915c0e.png)
此时有两张表:
一张是employee表,简称emp表(关联表,也从从表)
一张是department表,简称dep表(被关联表,也叫主表)
注意:被关联表称为主表,关联表(也就是设置外键的表)称为从表.
-
# 先建立主表
-
mysql>
create
table dep(
-
->
id
int primary
key,
-
->
name
varchar(
20)
not
null,
-
-> descripe
varchar(
20)
not
null
-
-> );
-
Query OK, 0 rows affected (0.07 sec)
-
-
# 再建立从表
-
mysql>
create
table emp(
-
->
id
int primary
key,
-
->
name
varchar(
20)
not
null,
-
-> age
int
not
null,
-
-> dep_id
int,
-
# 与主表建立关联, fk_dep为自定义的外键名,不可重复:
-
->
constraint fk_dep foreign
key(dep_id)
references dep(
id)
-
-> );
-
Query OK, 0 rows affected (0.05 sec)
-
-
mysql>
insert
into dep
values
-
-> (
1,
"技术部",
"各个大牛部们"),
-
-> (
2,
"销售部",
"一本正经胡说八道部门"),
-
-> (
3,
"财务部",
"花钱太多部门");
-
Query OK, 3 rows affected (0.00 sec)
-
Records: 3 Duplicates: 0 Warnings: 0
-
-
mysql>
insert
into emp
values
-
-> (
1,
'zyk',
19,
1),
-
-> (
2,
'xhh',
18,
2),
-
-> (
3,
'jein',
18,
3),
-
-> (
4,
'egon',
18,
2),
-
-> (
5,
'lisi',
18,
3);
-
Query OK, 5 rows affected (0.04 sec)
-
Records: 5 Duplicates: 0 Warnings: 0
-
-
# 如果从表中有与主表中关联的行,则不允许删除主表中的被关联的行
-
mysql>
delete
from dep
where
id=
1;
-
ERROR 1451 (23000): Cannot
delete
or
update a
parent
row: a foreign
key
constraint fails (
`db`.
`emp`,
CONSTRAINT
`fk_dep` FOREIGN
KEY (
`dep_id`)
REFERENCES
`dep` (
`id`))
-
-
# 删除从表中与主表中关联的的行(dep_id=
1)
-
mysql>
delete
from emp
where dep_id =
1;
-
Query OK, 1 row affected (0.03 sec)
-
-
# 此时可删除主表中的数据
-
mysql>
delete
from dep
where
id =
1;
-
Query OK, 1 row affected (0.01 sec)
-
-
mysql>
select *
from dep;
-
+
----+-----------+--------------------------------+
-
| id | name | descripe |
-
+
----+-----------+--------------------------------+
-
| 2 | 销售部 | 一本正经胡说八道部门 |
-
| 3 | 财务部 | 花钱太多部门 |
-
+
----+-----------+--------------------------------+
-
2 rows in
set (
0.00 sec)
同步表:
on delete cascade # 同步删除
on update cascade # 同步更新
-
# 主表内容如下
-
mysql>
select *
from dep;
-
+
----+-----------+--------------------------------+
-
| id | name | descripe |
-
+
----+-----------+--------------------------------+
-
| 1 | 技术部 | 各个大牛部们 |
-
| 2 | 销售部 | 一本正经胡说八道部门 |
-
| 3 | 财务部 | 花钱太多部门 |
-
+
----+-----------+--------------------------------+
-
3 rows in
set (
0.00 sec)
-
-
# 建立从表
-
mysql>
create
table emp(
-
->
id
int primary
key,
-
->
name
varchar(
20)
not
null,
-
-> age
int
not
null,
-
-> dep_id
int,
-
->
constraint fk_dep foreign
key(dep_id)
references dep(
id)
-
->
on
delete
cascade # 同步删除
-
->
on
update
cascade # 同步更新
-
-> );
-
Query OK, 0 rows affected (0.08 sec)
-
-
# 从表插入以下数据
-
mysql>
select *
from emp;
-
+
----+------+-----+--------+
-
| id | name | age | dep_id |
-
+
----+------+-----+--------+
-
| 1 | zyk | 19 | 1 |
-
| 2 | xhh | 18 | 2 |
-
| 3 | jein | 18 | 3 |
-
| 4 | egon | 18 | 2 |
-
| 5 | lisi | 18 | 3 |
-
+
----+------+-----+--------+
-
5 rows in
set (
0.00 sec)
-
-
-
# 同步更新
-
mysql>
update dep
set
id=
10
where
id=
1;
-
Query OK, 1 row affected (0.04 sec)
-
Rows matched: 1 Changed: 1 Warnings: 0
-
-
mysql>
select *
from dep;
-
+
----+-----------+--------------------------------+
-
| id | name | descripe |
-
+
----+-----------+--------------------------------+
-
| 2 | 销售部 | 一本正经胡说八道部门 |
-
| 3 | 财务部 | 花钱太多部门 |
-
| 10 | 技术部 | 各个大牛部们 |
-
+
----+-----------+--------------------------------+
-
3 rows in
set (
0.00 sec)
-
-
# 此时可见:与主表关联的dep_id也跟着变化了
-
mysql>
select *
from emp;
-
+
----+------+-----+--------+
-
| id | name | age | dep_id |
-
+
----+------+-----+--------+
-
| 1 | zyk | 19 | 10 |
-
| 2 | xhh | 18 | 2 |
-
| 3 | jein | 18 | 3 |
-
| 4 | egon | 18 | 2 |
-
| 5 | lisi | 18 | 3 |
-
+
----+------+-----+--------+
-
5 rows in
set (
0.00 sec)
-
-
-
# 同步删除
-
mysql>
delete
from dep
where
id=
3;
-
Query OK, 1 row affected (0.00 sec)
-
-
mysql>
select *
from dep;
-
+
----+-----------+--------------------------------+
-
| id | name | descripe |
-
+
----+-----------+--------------------------------+
-
| 2 | 销售部 | 一本正经胡说八道部门 |
-
| 10 | 技术部 | 各个大牛部们 |
-
+
----+-----------+--------------------------------+
-
2 rows in
set (
0.00 sec)
-
-
# 此时可见:与主表关联的dep_id也被删除了
-
mysql>
select *
from emp;
-
+
----+------+-----+--------+
-
| id | name | age | dep_id |
-
+
----+------+-----+--------+
-
| 1 | zyk | 19 | 10 |
-
| 2 | xhh | 18 | 2 |
-
| 4 | egon | 18 | 2 |
-
+
----+------+-----+--------+
-
3 rows in
set (
0.00 sec)
"