基础概念

连接数据库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
##链接----------------------------------------------------------
mysql -h主机地址 -P端口号 -u用户名 -p用户密码
#完整示例
mysql -h127.0.0.1 -P3306 -uroot -p123456
#省略(使用)默认端口号3306
mysql -h127.0.0.1 -uroot -p123456
#省略(使用)本地的MySQL服务器、默认端口号3306
mysql -uroot -p123456
#-p后面无密码,回车键后采用暗文形式输入密码
mysql -uroot -p

##退出(三种方式)----------------------------------------------------------
exit
quit
\q

数据库 - 增删查改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
##-------------------------------------------------创建数据库

#创建数据库
create database 数据库名;

#没有就创建,有同名就报错
create database sjkm;

#没有就创建,有同名就跳过
create database if not exists sjkm;

#数据库名称是关键字或者特殊字符#~@*&等,使用反引号
create database if not exists `create`;

#指定编码字符集-推荐
create database if not exists sjkm charset=字符集;
create database if not exists sjkm charset=utf8;

##-------------------------------------------------查看数据库

#查看所有数据库
show database;

#查看数据库全局默认编码
show variables like 'character_set_%';

#查看某个数据库的编码
show create database sjkm;

##-------------------------------------------------删除数据库

#删除数据库
drop database 数据库名;

#有就删除,没有就报错
drop database sjkm;

#有就删除,没有跳过
drop database if exists sjkm;

##-------------------------------------------------修改数据库

#修改某个数据库编码
alter database sjkm charset=utf8;

表 - 增删改查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#在对数据库的表进行操作的时候(增删改查),都必须先告诉MySQL我们要操作的是哪一个数据库
use 数据库名;

##---------------------------------------查看表

#查看所有表
show tables;

#查看某个表的结构
desc 表名;

##---------------------------------------创建表
create table 表名(
字段名称1 数据类型,
字段名称2 数据类型,
字段名称3 数据类型
);

#没有就创建,已有同名就报错
create table users(
id int,
name text,
age num
);

#没有就创建,已有同名就跳过
create table if not exists users(
id int,
name text,
age num
);

##---------------------------------------删除表
drop table 表名;

#有就删除,没有就报错
drop table users;

#有就删除,没有就跳过
drop table if exists users;

##---------------------------------------修改表

#修改表名
rename table stu to person;
rename table 原始名称 to 新的名称;

#添加字段
alter table 表名 add 新增字段名称 新增字段数据类型 [位置];
#默认添加在最后
alter table person add age int;
#添加最前面 - first
alter table person add score float first;
#添加在name字段的后面 - after xxx
alter table person add phone int after name;

#删除字段
alter table 表名 drop 字段名称;
alter table person drop phone;

#修改字段的数据类型
alter table 表名 modify 需要修改的字段名称 新的数据类型
alter table person modify score double;

#修改字段的名称和数据类型
alter table 表名 change 原始字段名称 新的字段名称 新的数据类型;
alter table person change age addr text;

数据 - 增删改查

image-20221219094059802

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
##---------------------------------------更新数据
update 表名 set 字段名称=值 [where 条件];

# 没有指定条件, 更新整张表中的数据
update stu set age=77;

# 指定了条件, 只会更新满足条件的数据
update stu set age=88 where name='张三';

# AND:指定多个条件, 需要同时满足
update stu set age=100 where name='张三' AND id=5;

# OR:指定多个条件, 谁满足谁被更新
update stu set age=66 where name='李四' OR name='王五';

# 字段名称=值,字段名称=值 更新多个字段的数据
update stu set name='小明', age=33 where id=5;


##---------------------------------------查询数据
select 字段名称1, 字段名称2 from 表名 [where 条件];

# 查询name字段的所有数据
select name from stu;

# 查询age大于22的所有数据
select * from stu where age > 22;

# 查询age大于22的id、name 字段数据
select id, name from stu where age > 22;

# 查询age是22或33的所有数据
select * from stu where age = 22 || age = 33;
select * from stu where age in (22, 52);

# 查询age是22到52之间的所有数据
select * from stu where age BETWEEN 22 AND 52;

# 查询age是空/不空的所有数据
select * from stu where age IS NOT NULL;
select * from stu where age IS NULL;


##---------------------------------------删除数据
delete from 表名 [where 条件];

#删除满足条件的数据
delete from stu where age > 33;

#删除所有的数据
delete from stu;

单表查询

1
2
#完整的格式,[]表示可选
select [查询选项] 字段名称 [from 表名] [where 条件] [order by 排序] [group by 分组] [having 条件] [limit 分页];

where支持的运算符

  • =(等于)、!=(不等于)、<>(不等于)、<(小于)、<=(小于等于)、>(大于)、>=(大于等于)

  • IN(xx, xx):固定的值

  • BETWEEN…AND:值在什么范围

  • IS NULL:(为空)

  • IS NOT NULL(不为空)

  • AND:与

  • OR:或

  • NOT:非

  • LIKE:模糊查询

结果集

通过查询语句查询出来的结果我们就称之为结果集。结果集以表的形式将查询的结果返回给我们。结果集返回的表和查询的表不是同一张表,被查询的表是真实存在的, 是存储在磁盘上的;而结果集不是真实存在的, 是存储到内存中的。

1
2
#查询指定字段数据时, 我们可以通过as给指定字段取别名
SELECT name as MyName, age as MyAge FROM stu;

image-20221220170421640

伪表

查询数据的时候, 除了可以查询指定字段的数据以外, 我们还可以查询表达式的结果

1
SELECT 6+6;

字段表达式虽然能够查询出表达式的结果, 但是不符合MySQL的规范;所以我们可以通过伪表(dual)的方式让字段表达式符合MySQL的规范

1
SELECT 6+6 from dual;

模糊查询

1
2
3
4
5
6
#格式
select 字段 from 表名 where 字段 like '条件';

#通配符
_ 表示任意一个字符
% 表示任意0~n个字符

image-20221220171335758

查询结果排序

1
2
3
4
5
6
7
8
#asc升序  desc降序
select 字段 from 表名 order by 字段 [asc | desc];

select * from stu order by age; #默认按照升序进行排序
select * from stu order by age asc; # 升序排序
select * from stu order by age desc; # 降序排序

select * from stu order by age desc, score asc; #如果年龄相同, 按照其它字段排序

image-20221220172105491

函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#1.聚合函数---------------------------------------------------------

count(); #统计
select count(*) from stu;
select count(*) from stu where score >= 60;

sum(); #求和
select sum(id) from stu;

avg(); #求平均值
select avg(id) from stu; # 21 / 6 = 3.5
select avg(score) from stu;

max(); #获取最大值
select max(score) from stu;

min(); #获取最小值
select min(score) from stu;

#2.数值类---------------------------------------------------------

rand(); #生成随机数
select rand() from dual;
select * from stu order by rand();

round()#四舍五入
select round(3.1) from dual;
select round(3.5) from dual;

ceil(); #向上取整
select ceil(3.1) from dual;

floor(); #向下取整
select floor(3.9) from dual;

truncate(); #截取小数位
select truncate(3.1234567, 2) from dual;

#3.字符串类---------------------------------------------------------
ucase(); #转换为大写
select ucase('hello world') from dual;

lcase(); #转换为小写
select lcase('HELLO WORLD') from dual;

left(); #从左边开始截取到指定的位置
select left('1234567890', 3) from dual;

right();#从右边开始截取到指定的位置
select right('1234567890', 3) from dual;

substring(); #从指定位置开始截取指定个字符
select substring('1234567890', 3, 5) from dual;

数据分组

1
2
3
4
5
6
7
8
9
10
select 分组字段 || 聚合函数 from 表名 group by 分组字段;

#查询到所有城市-有重复
select city from stu;
#按照城市分组
select city from stu group by city;
#统计每个城市中有多少人
select city, count(*) from stu group by city;
#统计每个城市中有哪些人(把name拼接成字符串)
select city, group_concat(name) from stu group by city;

image-20221220175201144

条件查询 having

having和where很像都是用来做条件查询的;但是where是去数据库中查询符合条件的数据,而having是去结果集中查询符合条件的数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
select * from stu where city='北京';
select * from stu having city='北京';

#例:查询平均年龄大于40的城市--------------------------------

#查询城市分组
select city from stu group by city;
#查询城市平均年龄
select city, avg(age) from stu group by city;
#给结果集的平均年龄这一列起一个好听的名字
select city, avg(age) as average from stu group by city;
#查询结果集中平均年龄大于40的城市
select city, avg(age) as average from stu group by city having average>=40;

image-20221220182241798

数据分页 limit

1
2
3
4
select 字段 from 表 limit 索引, 个数;

select * from stu limit 0, 3;
select * from stu limit 3, 3;

image-20221220184600603

查询选项

1
2
3
4
5
6
7
8
select [查询选项] 字段名称 from 表名;
all 显示所有查询出来的数据[默认]
distinct 去除结果集中重复的数据之后再显示

#distinct对结果集中重复的数据进行去重
#只有所有列的数据都相同才会去重
select distinct name from stu;
select distinct name, score from stu;

image-20221220191602098

多表查询

union

  • 返回的结果集的表头的名称是第一张表的名称

  • 使用union进行多表查询, 必须保证多张表查询的字段个数一致

  • 使用union进行多表查询, 默认情况下会自动去重

  • 使用union进行多表查询, 如果不想自动去重, 那么可以在union后面加上all
    #select id, name from stu union all select id, name from person;

1
2
3
4
5
#默认情况下多表查询的结果是笛卡尔集
select * from 表名1, 表名2;

#union:在纵向上将多张表的结果结合起来返回给我们
select * from 表名1 union select * from 表名2 union select * from 表名3;

表的连接查询

将多张表中’关联的字段’’连接’在一起查询我们称之为’表的连接查询’。大白话: 查询多张表中满足条件的数据

😍内连接 inner join

1
2
3
4
5
6
7
8
9
#返回满足条件的数据
select * from 表名1 inner join 表名2 on 条件;

select * from stu inner join grade on stu.id = grade.stuId;
#等同于
select * from stu, grade where stu.id = grade.stuId;

#在进行多表查询的时候, 如果想查询指定的字段, 那么必须在字段名称前面加上表名才行
select stu.id, stu.name, grade.score from stu inner join grade on stu.id = grade.stuId;

😍外链接

1
2
3
4
5
6
7
8
9
10
11
12
13
#左外连接 left join  -------------------------------------------
#在左外连接中, 左边的表是不看条件的, 无论条件是否满足, 都会返回左边表中所有的数据
#在左外连接中, 只有右边的表会看条件, 对于右边的表而言, 只有满足条件才会返回对应的数据
select stu.id, stu.name, grade.score from stu left join grade on stu.id = grade.stuId;
# 在以上的查询语句中stu表在左边, grade表在右边
# 所以stu表不看条件, 只有grade表看条件

#右外连接 right join -------------------------------------------
#在右外连接中, 右边的表是不看条件的, 无论条件是否满足, 都会返回右边表中所有的数据
#在右外连接中, 只有左边的表会看条件, 对于左边的表而言, 只有满足条件才会返回对应的数据
select stu.id, stu.name, grade.score from stu right join grade on stu.id = grade.stuId;
# 在以上的查询语句中stu表在左边, grade表在右边
# 所以grade表不看条件, 只有stu表看条件

😍交叉连接 cross join

1
2
3
4
5
#如果没有指定条件, 那么返回笛卡尔集
#select stu.id, stu.name, grade.score from stu cross join grade;

#如果指定了条件, 那么就等价于内连接
select stu.id, stu.name, grade.score from stu cross join grade on stu.id = grade.stuId;

自然连接

自然连接是用来简化’内连接和外连接’的。如果多张表需要判断的条件字段名称一致, 那么不用编写条件, 自然连接会自动判断

  • 如果没有指定条件, 也没有同名的字段, 那么就会返回笛卡尔集

  • 在自然连接中, 返回的结果集会自动优化, 会自动去除重复的判断字段

1
2
3
4
5
6
7
8
9
10
11
select * from stu inner join grade on stu.stuId = grade.stuId;

#自然内连接
select * from 表名1 natural join 表名2;
select * from stu natural join grade;

#自然左外连接
select * from stu natural left join grade;

#自然右外连接
select * from stu natural right join grade;

using 简化

如果多张表需要判断的条件字段名称一致, 那么除了可以使用自然连接来简化以外,还可以使用using关键字来简化。

1
2
3
4
5
6
7
8
9
10
11
#内连接
select * from stu inner join grade on stu.stuId = grade.stuId;
select * from stu inner join grade using(stuId);

#左外连接
select * from stu left join grade on stu.stuId = grade.stuId;
select * from stu left join grade using(stuId);

#右外连接
select * from stu right join grade on stu.stuId = grade.stuId;
select * from stu right join grade using(stuId);

子查询😍

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#将一个查询语句查询的结果作为另一个查询的条件来使用--------------------------------------------------

#标准子查询(返回的结果只有一个)
select stuId from grade where score = 100;#查询一百分数的学生ID
select name from stu where stuId = 3; #查询一百分学生ID的姓名
#=》
select name from stu where stuId = (select stuId from grade where score = 100);

#非标准子查询(返回的结果有多个)
select stuId from grade where score >= 60;#查询60分以上的学生ID
select name from stu where stuId = 3 OR stuId = 1; #查询60分以上的学生ID的姓名(方法1)
select name from stu where stuId in(3, 1);#查询60分以上的学生ID的姓名(方法2)
#=》
select name from stu where stuId in(select stuId from grade where score >= 60);

#将一个查询语句查询的结果作为另一个查询的表来使用-------------------------------------------------
select name, city, score from person where score >= 60;#查询person表中分数大于60的姓名、城市、分数
select name, city, score from (select name, city, score from person where score >= 60) as t;
#注意点:如果要将一个查询语句查询的结果作为另一个查询的表来使用, 那么必须给子查询起一个别名

数据类型

如果没有数据类型, 那么MySQL就不知道需要分配多大的存储空间来存储你的数据过大会浪费会造成资源浪费, 体积变大效率变低, 过小可能导致数据溢出不能完整的保存数据。

整型类型/浮点类型/定点类型/字符类型/文本类型/枚举类型/集合类型/日期类型/布尔类型

整型类型 - 整数

默认情况下整型就是有符号的,例如TINYINT只能保存-128到127之前的数据,在保存数据的时候, 如果超出了当前数据类型的范围, 那么就会报错。我们可以在数据类型的后面加上 unsigned 来将数据类型变成无符号的,即保存0到255之间的整数。

1
2
3
4
create table users(
id int,
age tinyint unsigned
);
  • TINYINT 1 字节 (-128,127) (0,255) 小整数值

  • SMALLINT 2 字节 (-32768,32767) (0,65535) 大整数值 - 6万

  • MEDIUMINT 3 字节 (-8388608,8388607) (0,16777215) 大整数值 - 1600万

  • INT或INTEGER 4 字节 (-2147483648,2147483647) (0,4294967295) 大整数值 - 42亿

  • BIGINT 8 字节 (-9223372036854775808,9223372036854775807) (0,18446744073709551615) 极大整数值

在设置整型的时候, 还可以设置整型数据将来显示的位宽;如果存储的数据没有指定的位宽宽, 那么就会自动补空格或者0, 如果大于或者等于了指定的位宽, 那么毛都不做。

1
2
3
4
5
#tinyint(2)指定位宽2,默认补齐空格;后面加上zerofill补齐0
create table person3(
id int,
age tinyint(2) zerofill
);

浮点类型 - 不准确小数

和其它编程语言中一样, 浮点类型是不准确的,所以在企业开发中千万不要使用浮点数来保存用户的准确(珍贵)信息(RMB)

  • FLOAT(m, d) 4 字节 单精度

  • DOUBLE(m, d) 8 字节 双精度

  • m总位数, d小数位数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#以插入1.12345678901234567890为例

#默认保留的小数位数不同
#weight: 1.12346
#height: 1.1234567890123457
create table person(
id int,
weight FLOAT,
height DOUBLE
);

#手动指定小数的总位数m和小数部分的位数d
#weight: 1.123457
#height: 1.123457
create table person2(
id int,
weight FLOAT(10, 6),
height DOUBLE(10, 6)
);

#保存数据的有效精度也不同
#weight: 1.123456-8357467651000
#height: 1.123456789012345-7000
create table person3(
id int,
weight FLOAT(20, 19),
height DOUBLE(20, 19)
);

定点类型 - 精准小数

定点类型的本质: 是将数据分为两个部分来存储, 每个部分都是整数。所以定点数不要滥用, 因为非常消耗资源

  • decimal(M, D)
1
2
3
4
5
6
7
8
9
#以插入1.12345678901234567890为例

create table person4(
id int,
weight decimal(21, 20),
height decimal(21, 20)
);
#weight: 1.12345678901234567890
#height: 1.12345678901234567890

字符类型 - 字符

  • CHAR(size) 0-255 字节 定长字符串

  • VARCHAR(size) 0-65535字节 变长字符串

char和varchar区别:能够保存数据的容量不一样;char不会回收多余的字符, 要多少给多少;varchar会回收多余的字符, 用多少给多少

  • 通过 char(2)存储存储数据’a’, 存储的结果是’ a’;

  • 通过 varchar(2)存储存储数据’a’, 存储的结果是’a’;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#示例一:只要超出申请的范围就会报错
create table person(
id int,
name1 char(2),
name2 varchar(2)
);
insert into person values (1, 'a', 'b');
insert into person values (1, '12', '34');
insert into person values (1, 'abc', 'def');

#不指定默认最大字节,char为255,varchar理论上最大为65535字节
# 65535 / 3 = 21845, 由于utf8一个字符占用3个字节, 所以varchar在utf8的表中最多只能存储21845个字符
# 65535 / 2 = 32767, 由于gbk一个字符占用2个字节,所以varchar在gbk的表中最多只能存储32767个字符
create table person2(
id int,
name1 char,
name2 varchar
)charset=gbk;

#MySQL中每一行存储的数据是有大小限制的, 每一行最多只能存储65534个字节.超过就会报错
#注意是一行,而不是单元格。如下为(21845+3)*3 = 65544 字节
create table person(
name1 char(3),
name2 varchar(21845) #在UTF8中相当于65535个字节
)charset=utf8;
# 报错:Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. This includes storage overhead, check the manual. You have to change some columns to TEXT or BLOBs

大文本类型

  • TINYTEXT 0-255字节 短文本字符串

  • TEXT 0-65535字节 长文本数据

  • MEDIUMTEXT 0-16777215字节 中等长度文本数据

  • LONGTEXT 0-4294967295字节 极大文本数据

1
2
3
4
5
6
create table person(
name1 char(3),
name2 TEXT
#不会报错, 因为没有超出显示, 实际只占用10个字节
)charset=utf8;
#大文本类型在表中并不会实际占用所能保存的字节数, 而是利用10个字节引用了实际保存数据的地址

枚举类型

MySQL中的枚举类型和其它的编程语言一样, 底层都是使用整型来实现的。和其它编程语言不太一样的是, 其它编程语言的枚举都是从0开始的, 而MySQL的枚举是从1开始的。

  • enum(值1, 值2, …)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#创建一张表,gender字段为枚举类型
create table person(
id int,
gender enum('男', '女', '保密')
);
#插入数据
insert into person values (1, '火'); #会报错
insert into person values (2, '男'); #不会报错,成功插入

#由于MySQL的枚举底层是使用整型实现的, 所以我们在赋值的时候除了可以赋值固定的几个值以外
#我们还可以赋值对应的整数
# select gender+0 from person;
insert into person values (3, 1); #1代表男,不会报错
insert into person values (4, 4); #会报错

集合类型

和编程开发中一样, 如果某个字段的取值只能是几个固定值中的几个, 那么就可以使用集合类型

  • set(值1, 值2, …)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#创建表
create table person(
id int,
hobby set('篮球','足球','高尔夫球','足浴')
);

#插入数据
insert into person values (1, '篮球,足球,高尔夫球'); #不会报错
insert into person values (1, '橄榄球'); #会报错

#集合类型的底层也是使用整型实现的
# select hobby+0 from person;
insert into person values (2, '篮球'); #1
insert into person values (3, '足球'); #2
insert into person values (4, '高尔夫球'); #4
insert into person values (5, '足浴'); #8
insert into person values (6, '篮球,足球,高尔夫球'); #1+2+4=7
#MySQL的集合类型是按照2(n)的方式来实现的
#篮球在set(..)中的索引是0,依次类推
#篮球 2(0) = 1
#足球 2(1) = 2
#高尔夫球 2(2) = 4
#足浴 2(3) = 8

布尔类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
create table person(
id int,
vip boolean
);
#在表中,true显示为1,false显示为0
insert into person values (1, '男'); #会报错
insert into person values (1, true); #不会报错
insert into person values (2, false); #不会报错

#MySQL中的布尔类型也是使用整型来实现的, 0就表示假, 1就表示真
#底层的本质是因为MySQL是使用C/C++来实现的, 所以就是'非零即真'
insert into person values (3, 1); #不会报错
insert into person values (4, 0); #不会报错
insert into person values (5, 2); #不会报错,显示2

日期类型

在存储时间的时候, 需要用单引号将时间括起来。否则会报错

  • DATE 3字节 YYYY-MM-DD 日期值
  • TIME 3字节 HH:MM:SS 时间值或持续时间
  • DATETIME 8字节 YYYY-MM-DD HH:MM:SS 混合日期和时间值
1
2
3
4
5
6
7
create table person(
id int,
filed1 DATE,
filed2 TIME,
filed3 DATETIME
);
insert into person values (1, '2020-02-02', '14:18:23', '2020-02-02 14:18:23');

数据完整性

什么是数据的完整性?

  • 保证保存到数据库中的数据都是正确的。

如何保证数据完整性?

  • 数据的完整性可以分为三类: 实体完整性、域完整性、参照完整性

  • 无论是哪一种完整性都是在创建表时给表添加约束即可

实体完整性

什么是实体?

  • 表中的一行数据就是一个实体(entity)

如何保证实体完整性?

  • 保证实体完整性就是保证每一行数据的唯一性

实体完整性的约束类型

  • 主键约束(primary key)
  • 唯一约束(unique)
  • 自动增长列(auto_increment)

主键约束 primary key

主键用于唯一标识表中的每一条数据, 和现实生活中的身份证很像。主键的特征:

  • 如果将某一个字段设置成了主键, 那么这个字段的取值就不能重复了

  • 如果将某一个字段设置成了主键, 那么这个字段的取值就不能是null了

  • 一张表中只能有一个主键, 不能出现多个主键

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#创建表
create table person(
id int primary key, #方法一
name varchar(20)
);

create table person3(
id int,
name varchar(20),
primary key(id) #方法二
);

#插入数据
insert into person values (1, '百度'); #可
insert into person values (1, '淘宝'); #报错
insert into person values (2, '百度'); #可

联合主键:

我们通过将表中的某个永远不重复的字段设置为主键, 从而达到保证每一行数据的唯一性(实体完整性)。但是在企业开发中有时候我们可能找不到不重复的字段, 此时我们还可以通过联合主键的方式来保证每一行数据的唯一性。

联合主键就是同时将多个字段作为一个主键来使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 ________
|name| age|
|lnj | 88 |
|zs | 88 |
|lnj | 33 |
#------------------------------------------
create table person(
name varchar(20),
age int,
#设定联合主键
primary key(name, age)
);
insert into person values ('lnj', 88); #报错
insert into person values ('lnj', 99); #可

#联合主键并不是添加多个主键, 而是将多个字段的值作为主键来使用
#也就是过去我们指定id为主键, 那么id的取值不能重复
#而现在如果我们指定 name和age为主键, 那么name+age的值不能重复

唯一约束 unique

唯一约束用于保证某个字段的值永远不重复。主键约束和唯一约束异同:

  • 唯一约束和主键约束一样, 被约束的字段的取值都不能够重复

  • 主键在一张表中只能有一个, 而唯一约束在一张表中可以有多个

  • 主键的取值不能为Null, 而唯一约束的取值可以是Null

1
2
3
4
5
6
7
8
create table person(
id int unique,
name varchar(20) unique
);

insert into person values (1, '张三');#可
insert into person values (2, '张三');#报错
insert into person values (3, null); #报错

自动增长约束 auto_increment

自动增长约束的作用是让某个字段的取值从1开始递增, 从而保证实体完整性

1
2
3
4
5
6
7
8
9
#如果某个字段是自动增长的, 那么这个字段必须是主键才可以,否则报错
create table person(
id int auto_increment primary key,
name varchar(20)
);

insert into person values (1, '张三'); #可,指定id为1
insert into person values (null, '李四'); #可,id自动增长
insert into person values (default, '麻子');#可,id自动增长
1
2
3
4
5
在企业开发中我们应该如何选择主键?
最少性: 能用一个字段作为主键, 就不要使用多个字段
稳定性: 能用不被操作(修改)的字段作为主键, 就不要使用会被操作的字段作为主键

一般情况下我们会定义一个名称叫做id的字段, 并且这个字段是整型的, 并且这个字段是自动增长的来作为主键

修改约束

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
create table person(
id int,
name varchar(20)
);

#修改成主键约束------------------------------------------
alter table 表名 add primary key(字段);
alter table person add primary key(id);

#修改成唯一约束------------------------------------------
alter table 表名 add unique(字段);
alter table person add unique(name);

#修改成自动增长约束---------------------------------------
alter table 表名 modify 字段名称 数据类型 auto_increment;
#必须先把字段设置为主键约束
alter table person add primary key(id);
#再修改成自动增长
alter table person modify id int auto_increment;

域完整性

什么是域?

  • 一行数据中的每个单元格都是一个域

如何保证域的完整性?保证域的完整性就是保证每个单元格数据的正确性

  • 使用正确的数据类型

    • 例如: 人的年龄不可能超过255岁, 而且不能是负数, 所以我们就可以使用 TINYINT UNSIGNED

    • 例如: 人的性别只能是男/女或者妖, 所以我们就可以使用枚举类型

    • 例如: 要存储比较多的文字, 为了保证不超出每一行最大的存储限制, 我们就可以使用大文本类型

  • 使用非空约束(not null)

  • 使用默认值约束(default)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#非空约束------------------------
create table person(
id int,
name varchar(20) not null
);
insert into person values (1, null); #报错

#默认值约束----------------------
create table person(
id int,
name varchar(20) default 'user'
);
#注意点: 哪怕设置了默认值, 传入null之后也不会使用默认值
insert into person2 values (1, null); #null
#使用default
insert into person2 values (1, default); #user
insert into person2 values (1, 'zs'); #zs

参照完整性

参照完整性又称引用完整性, 主要用于保证多表之间引用关系的正确性

表与表之间的关系可以分为三种:

  • 一对一:一夫一妻制
  • 一对多: 一个人有多个汽车,一个班有多个学生,一个人有多们成绩
  • 多对多:一个学生有多个老师, 一个老师有多个学生

默认情况下表与表之间是独立存在的, 不会相互影响;也正是因为如此, 默认情况下也不会检查表与表之间的依赖关系。所以为了保证表与表之间参照完整性, 我们可以通过’外键’来保证参照完整性。

定义外键

如果一张表中有一个字段指向了别的一张表中的主键,就将该字段叫做外键

例如: 成绩表中的uid引用了学生表中的id, 那么成绩表中的uid我们就称之为外键

  • 只有InnoDB的存储引擎才支持外键约束

  • 外键的数据类型必须和指向的主键一样

  • 在一对多的关系中, 外键一般定义在多的一方(一个学生有多门成绩, 那么外键定义在成绩表中)

  • 定义外键的表我们称之为从表, 被外键引用的表我们称之为主表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#定义外键 - 详见下方外键的操作
#严格操作
foreign key(外键字段名称) references 主表名称(主表主键名称)
#置空操作
foreign key(外键字段名称) references 主表名称(主表主键名称) on delete set null
#级联操作
foreign key(外键字段名称) references 主表名称(主表主键名称) on update cascade

#---------------------------------------------------------------

|--------stu---------| |---------grade---------|
| id | name | gender | | id | km | score | uid |
| 1 | 张三 | 男 | | 1 |语文 | 100 | 1 |
| 2 | 李四 | 女 | | 2 |数学 | 99 | 1 |
|--------------------| | 3 |英语 | 98 | 1 |
| 4 |语文 | 60 | 2 |
| 5 |数学 | 59 | 2 |
| 6 |英语 | 58 | 2 |
|-----------------------|

#---------------------------------------------------------------

#创建主表
create table stu(
id int auto_increment primary key,
name varchar(20),
gender enum('男','女','保密')
);
#创建从表
create table grade(
id int auto_increment primary key,
km varchar(20),
score double,
uid int,
foreign key(uid) references stu(id) #定义外键
);

insert into grade2 values (null, '生物', 99, 1);
insert into grade2 values (null, '生物', 99, 3);#报错,stu中没有id为3的学生

动态修改外键

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
create table grade(
id int auto_increment primary key,
km varchar(20),
score double,
uid int
);

#添加外键------------------------------------------------------------------------
alter table 从表名称 add foreign key(外键字段名称) references 主表名称(主表主键名称);
alter table grade add foreign key(uid) references stu(id);


#查看外键------------------------------------------------------------------------
show create table 从表名称;
show create table grade;

#得到的结果中有一行为:
#CONSTRAINT `grade_ibfk_1` FOREIGN KEY (`uid`) REFERENCES `stu` (`id`)
#将uid变成外键, 外键的名称是grade_ibfk_1。uid的取值引用的是stu这张表中的id字段的值


#删除外键------------------------------------------------------------------------
alter table 从表名称 drop foreign key 外键名称;
alter table grade drop foreign key grade_ibfk_1;

外键的操作

😍严格操作

定义外键时(前面讲的)默认是严格操作

1
2
foreign key(外键字段名称) references 主表名称(主表主键名称)
foreign key(uid) references stu(id)
  • 主表不存在对应数据,从表不允许添加
  • 从表引用着数据,主表不允许删除
  • 从表引用着数据, 主表不允许修改

😍置空操作 null

删除主表数据的同时删除从表关联的数据

1
2
foreign key(外键字段名称) references 主表名称(主表主键名称) on delete set null
foreign key(uid) references stu(id) on delete set null

😍级联操作(cascade)

修改主表数据的同时修改从表关联的数据

1
2
foreign key(外键字段名称) references 主表名称(主表主键名称) on update cascade
foreign key(uid) references stu(id) on update cascade

😍一般情况下主表删除时从表置空, 主表更新时从表级联

1
[constraint 外键名称] foreign key(外键字段) references 主表(主键)[主表删除的动作][主表更新的动作]

多对多外键

依次一个一个的添加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
        学生表stu                  关系表rel                 教师表teacher
|-----------------------| |---------------------| |-----------------------|
| id | stuName | gender | | stuId | teacherId | | id | stuName | gender |
| 1 | 张三 | 男 | | 1 | 1 | | 1 | 王五 | 男 |
| 2 | 李四 | 女 | | 1 | 2 | | 2 | 赵六 | 女 |
|-----------------------| | 1 | 3 | | 3 | 周七 | 男 |
| 2 | 1 | |-----------------------|
| 2 | 2 |
| 2 | 3 |
|---------------------|
一名学生有多名教师,一名教师有多名学生
#------------------------------------------------------------------------
#创建三张表
create table stu(
id int auto_increment primary key,
name varchar(20),
gender enum('男','女','妖')
);
create table teacher(
id int auto_increment primary key,
name varchar(20),
gender enum('男','女','妖')
);
create table rel(
stuId int,
teacherId int
);

#动态添加外键
alter table 从表名称 add foreign key(外键字段名称) references 主表名称(主表主键名称);
#关系表中的stuId(外键)对应学生表中的id(主键)
alter table rel add foreign key(stuId) references stu(id);
#关系表中的teacherId(外键)对应教师表中的id(主键)
alter table rel add foreign key(teacherId) references teacher(id);

事务

事务的本质是开启事务的时候拷贝一张一模一样的表。然后执行相关的操作都是在拷贝的这张表中做操作。如果失败了, 如果执行了rollback, 那么系统就会自动删除拷贝的这张表。所以失败了不会影响到原有的数据。如果成功了, 如果执行了commit, 那么系统就会自动利用拷贝表中最新的数据覆盖原有表中的数据。所以成功了会影响到原有的数据

  • MySQL中的事务主要用于处理容易出错的数据。

  • 事务可以用来维护数据库的完整性,保证成批的 SQL 语句要么全部执行,要么全部不执行。

  • 事务用来管理 insert,update,delete 语句

  • MySQL中只有使用了 Innodb 数据库引擎的表才支持事务。

语法

  • 开启事务: start transaction

  • 提交事务: commit

  • 回滚事务: rollback

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#示例:创建数据---------------------------------------------------------
create table bank(
id int unsigned auto_increment primary key,
cardid varchar(4),
name varchar(20),
money int
);
insert into bank values (null, '1001', 'zs', 1000), (null, '1002', 'ls', 1000);

#使用事务---------------------------------------------------------
start transaction; #开启事务
update bank set money=money-1000 where cardid='1002';
update bank set money=money+1000 where cardid='1001';
commit; #任务成功, 提交开启事务之后所有操作

事务回滚点

  • savepoint 回滚点名称

  • rollback to 回滚点名称

1
2
3
4
5
6
7
start transaction;#开启事务
insert into bank values (null, '1003', '333', 33333);
savepoint abc; #设置回滚点
insert into bank values (null, '1003', '444', 44444);
savepoint def; #设置回滚点
insert into bank values (null, '1003', '555', 55555);
rollback to abc; #回到回滚点

事务特点

原子性(关注的是状态):

  • 事务开启后的所有操作,要么全部成功,要么全部失败,不可能出现部分成功的情况
  • 事务执行过程中如果出错,哪怕我们不手动回滚, 系统也会自动帮我们回滚

一致性(关注数据可见性):

  • 事务开始前和结束后,数据库的完整性约束没有被破坏
  • 例如 A向B转账,不可能A扣了钱,B却没收到

持久性:

  • 事务完成后,事务对数据库的所有操作是永久的, 操作完成之后就不能再回滚

隔离性:

  • 数据库允许多个并发事务同时对其数据进行读写和修改的能力,
  • 隔离性可以防止多个事务并发时由于交叉执行而导致数据的不一致。

事务隔离级别

  • 读未提交(read uncommitted): 一个事务可以读取另一个未提交事务的数据

  • 读提交(read committed): 一个事务要等另一个事务提交后才能读取数据

  • 可重复读(repeatable read): 一个事务范围内多个相同的查询返回相同的结果

  • 串行化(serializable): 前面一个事务没有执行完后面一个事务不能执行

1
2
3
4
5
6
7
8
9
10
11
#查看隔离级别----------------------------------------------
#全局的
select @@global.transaction_isolation;
#当前会话的
select @@transaction_isolation;

#设置隔离级别----------------------------------------------
#全局的
set global transaction isolation level 级别名称;
#当前会话
set session transaction isolation level 级别名称;

每个事务格力级别的优缺点:

1
2
3
4
5
事务隔离级别                  脏读    不可重复读     幻读
读未提交(read-uncommitted) 是 是 是
不可重复读(read-committed) 否 是 是
可重复读(repeatable-read) 否 否 是
串行化(serializable) 否 否 否

脏读、不可重复度、幻读示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
- 脏读
能读取到其它事务没有提交的数据
示例:
A客户端:
set session transaction isolation level read uncommitted;
start transaction;
update bank set money=money-1000 where cardId='1002';
select * from bank;
B客户端:
set session transaction isolation level read uncommitted;
select * from bank;

- 解决办法(read committed)
A客户端:
set session transaction isolation level read committed;
start transaction;
update bank set money=money-1000 where cardId='1002';
select * from bank;
B客户端:
set session transaction isolation level read committed;
select * from bank;
#-------------------------------------------------------------------------------------------------


- 不可重复读
一个事务范围内多次查询的结果不同
示例:
A客户端:
set session transaction isolation level read committed;
start transaction;
select * from bank;
B客户端:
set session transaction isolation level read committed;
start transaction;
update bank set money=money-1000 where cardId='1002';
commit;
A客户端:
select * from bank;
commit;
- 解决办法(repeatable read)
#-------------------------------------------------------------------------------------------------


- 重复读
一个事务范围内多次查询的结果相同
A客户端:
set session transaction isolation level repeatable read;
start transaction;
select * from bank;
B客户端:
set session transaction isolation level repeatable read;
start transaction;
update bank set money=money-1000 where cardid='1002';
commit;
A客户端:
select * from bank;
commit;
- 解决办法(serializable)
#-------------------------------------------------------------------------------------------------


- 幻读
读到到的结果并不是最终的结果
A客户端:
set session transaction isolation level serializable;
start transaction;
select * from bank;
B客户端:
set session transaction isolation level serializable;
start transaction;
update bank set money=money-1000 where cardid='1002';
commit;
A客户端:
select * from bank;
commit;

视图

视图本质就是将结果集缓存起来;由于结果集是一张虚拟的表, 所以视图也是一张虚拟的表;由于结果集是建立在表的基础上的, 所以视图也是建立在表的基础上的

  • 视图可以用来简化SQL语句
  • 视图可以用来隐藏表的结构
  • 视图可以用来提升数据安全性
1
2
3
create [algorithm={merge||temptable||undefined}] view 视图名称 as select 语句 [with check option];
#例:
CREATE VIEW person_view as SELECT name, city FROM person

增删改查

由于视图保存的是结果集,由于结果集是基于原始表的,所以操作视图中的数据, 本质上操作的是原始表中的数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#视图数据操作--------------------------------------------------------------------------------
SELECT name, city FROM person; #查询person中的name和city
CREATE VIEW person_view as SELECT name, city FROM person; #创建视图
SELECT * FROM person_view; #查询视图
INSERT INTO person_view values ('it666', '武汉'); #向视图中增加数据
UPDATE person_view set city='香港' WHERE name='it666'; #修改视图数据
DELETE FROM person_view WHERE name='it666'; #删除视图数据

#修改视图内容--------------------------------------------------------------------------------
alter view 视图名称 as select 语句;
alter view person_view as select name, score from person; #将这张视图重新显示为name和score

#删除视图-----------------------------------------------------------------------------------
drop view [if exists] 视图名;

视图算法 algorithm=

merge: 合并式(替代式)算法。将视图的语句和外层的语句合并之后再执行;该算法允许更新数据

temptable: 临时表(具代式)算法。将视图生成一个临时表, 再执行外层的语句;该算法不允许更新数据

undefined: 未定义算法。由MySQL自己决定使用如上的哪一种算法, 默认就是undefined;一般情况下会自动选择merge算法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#merge-----------------------------------------------
CREATE algorithm=merge VIEW person_view1 AS SELECT name, city FROM person;

SELECT * FROM person_view1;
# ↑查询视图,相当于下面这样↓
SELECT * FROM (SELECT name, city FROM person) as t;

#temptable-----------------------------------------------
CREATE algorithm=temptable VIEW person_view2 AS SELECT name, city FROM person;

SELECT * FROM person_view2;
# ↑查询视图,相当于下面这样↓
(SELECT name, city FROM person) as t;
SELECT * FROM t;

视图限制 with check option

默认情况下哪怕插入的数据和更新的数据不符合创建视图条件, 我们也是可以通过视图来插入和更新的。如果想让插入和更新的数据必须符合创建视图的条件, 那么就可以在创建视图的时候添加限制条件with check option

1
2
3
4
SELECT name, city, score FROM person WHERE score >= 60;
CREATE VIEW person_view AS SELECT name, city, score FROM person WHERE score >= 60 with check option;
INSERT INTO person_view values('it666', '台湾', 33); #报错, 不符合scroe>=60条件
UPDATE person_view set score=55 WHERE name='ww'; #报错, 不符合scroe>=60条件

如果视图的算法是merge算法, 那么可以更新视图

如果没有指with check option, 那么无论数据符不符合创建视图条件都可以更新

如果指定了with check option, 那么只有符合创建视图条件才可以更新

除此之外由于视图是一张虚拟表, 视图是基于原始表的, 更新视图的本质就是更新原始表;所以只有原始表中存在的原始数据才可以更新, 通过其它方式生成的数据都不可以更新

1
2
3
4
5
6
7
8
9
10
11
12
13
SELECT city, avg(score) as avgScore FROM person GROUP BY city;
CREATE VIEW person_view AS SELECT city, avg(score) as avgScore FROM person GROUP BY city;
SELECT * FROM person_view;
UPDATE person_view set avgScore=11 WHERE city='北京'; #报错

更新限制
聚合函数
DISTINCT关键字
GROUP BY子句
HAVING子句
UNION运算符
FROM子句包含多张表
SELECT语句中应用了不可更新的形势图

预处理

所谓的预处理技术,最初也是由MySQL提出的一种减轻服务器压力的一种技术!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#传统mysql处理流程---------------------------------------------------------
1, 在客户端准备sql语句
select * from stu where id=1;
select * from stu where id=2;
2, 发送sql语句到MySQL服务器
3, MySQL服务器对sql语句进行解析(词法,语法), 然后编译, 然后执行该sql语句
4, 服务器将执行结果返回给客户端
弊端:
- 哪怕多次传递的语句大部分内容都是相同的, 每次还是要重复传递
- 哪怕语句是相同的, 每次执行之前还是要先解析、编译之后才能执行

#预处理的处理流程---------------------------------------------------------
1, 在客户端准备预处理sql语句
prepare 预处理名称 from 'sql语句';
prepare stmt from 'select * from stu where id=?;';
2, 发送预处理sql语句到MySQL服务器
3. MySQL服务器对预处理sql语句进行解析(词法,语法), 但不会执行
4. 在客户端准备相关数据
set @id=1;
5. MySQL服务器对数据和预处理sql编译, 然后执行该sql语句
execute stmt using @id;
6. 服务器将执行结果返回给客户端
优点:
- 只对sql语句进行了一次解析
- 重复内容大大减少(网络传输更快)

#演示
prepare stmt from 'select * from stu where id=?;';
set @id=1;
execute stmt using @id;
set @id=2;
execute stmt using @id;

存储过程

存储过程和其它编程语言的函数很像, 可以用于封装一组特定功能的SQL语句集

用户通过call 存储过程的名称()'来调用执行它。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
-- 创建
create procedure 存储过程名称(形参列表)
begin
-- sql语句
end;

-- 调用
call 存储过程名称(参数);

---------------------------------示例

-- 创建
create procedure show_stu_by_id(stuId int)
begin
select * from stu where id=stuId;
end;

-- 调用




索引

索引就相当于字典中的目录(拼音/偏旁部首手),有了目录我们就能通过目录快速的找到想要的结果。但是如果没有目录(拼音/偏旁部首手), 没有索引,那么如果想要查找某条数据就必须从前往后一条一条的查找,所以索引就是用于帮助我们提升数据的查询速度的。

存储引擎

MySQL中的存储引擎就好比我们现实生活中的银行, 不同的银行提供的安全级别、服务水平、存储功能不一样;不同的存储引擎提供的安全级别、服务水平、存储功能等也不一样。

  • MyISAM:安全性低, 但不支持事务和外键, 适合频繁插入和查询的应用

  • InnoDB(默认):安全性高, 支持事务和外键, 适合对安全性, 数据完整性要求较高的应用

  • Memory:访问速度极快, 但不会永久存储数据, 适合对读写速度要求较高的应用

1
2
3
4
5
6
7
8
9
#创建表指定存储引擎
create table stu(
id int,
name text
)engine=引擎名称;

#修改表的存储引擎
alter table 表名 engine=引擎名称;
alter table stu engine=MyISAM;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
不同引擎本质
- 前面我们说过数据库的本质就是文件, 所以我们可以先观察一下
- 通过我们的观察, 我们发现只要创建一个数据库就会自动创建一个文件夹
- 通过我们的观察, 我们发现只要创建一张表就会在指定的数据库文件夹中创建一个文件
- 创建表的时候自动创建的这个文件就保存了这张表的结构

InnoDB:
- 如果表的存储引擎是InnoDB, 那么只要创建表就会自动创建一个文件, 这个文件就保存了这张表的结构
- 如果往InnoDB的表中存储数据, 那么数据会被存储到ibdata1的文件中, 如果存储的数据比较多, 那么系统会自动再创建ibdata2, ibdata3, ...文件

MyISAM:
- 如果表的存储引擎是MyISAM, 那么只要创建表就会自动创建三个文件
+ .sdi这个文件就保存了这张表的结构
+ .MYD这个文件就保存了这张表中存储的数据
+ .MYI这个文件就保存了这张表中的索引

Memory:
- 如果表的存储引擎是Memory, 那么只要创建表就会自动创建一个文件, 这个文件就保存了这张表的结构
- 注意点: 如果表的存储引擎是Memory, 那么就不会像InnoDB/MyISAM将数据保存到文件中了, 而是直接保存到内存中

mysql默认会创建四个库:

  • information_schema:保存着关于mysql服务器所维护的其它数据库的信息。如数据库名,数据库的表,表栏的数据类型与访问权限等
  • mysql:MySQL系统数据库,保存了登录用户名,密码,以及每个用户的访问权限等
  • performance_schema:用来保存数据库服务器性能的参数
  • sys:这个库通过视图的形式把information_schema和performance_schema结合起来,查询出更加令人容易理解的数据