B站 动力节点 老杜 老师 的课堂笔记
狂赞老杜老师

此笔记虽说是MySQL基础,但是学完基本可以用到工作。咱又不是专门搞数据库工作的~

DQL(查询)

select的简单语法

show databases;—>显示已有数据库(注意后面的s)
create database test;—>建立一个数据库,名叫test(注意没有s)
use test;—>使用test数据库
source 链接;—>导入数据(表等)
表是数据库最基本的单位(行 为字段)
desc test;—>查看字段描述(describe)
理解相对路径和绝对路径,在MySql中尤其是绝对路径和其他的有很大区别

单表查询(DQL(数据查询语言))

select … from … where … group by … having … order by …;

在MySQL中一定要以分号结尾

执行顺序:

  1. from(表单来源)
  2. where(判断筛选)
  3. group by(分组)
  4. having(用于分组函数情况下的判断筛选)
  5. order by(排序)

语法细节

select * from student;—>用来查询student表单中的所有字段对应的数据

select math from student;—>用来查询student表单中的math字段对应的数据

select math,name from student;—>用来查询student表单中的math字段和name对应的数据(多个字段用逗号隔开)

select math as m from student;—>用来查询student表单中的math字段对应的数据并且将math字段改名为m,因为这里时select(查询)所以不会修改数据库

select math m from student;—>上一句的简写

select math from student where math > 60;—>判断语句,math字段对应数据大于60(即满足要求)才能显示出来

判断语句:

  • <
  • >
  • >=
  • <=
  • =
  • !=
  • <>(和!=效果一样)
  • and(和编程语言的&&一个效果)
  • or(和编程语言的||一个效果)
  • in(包含,其实也算是一个函数,其参数不是表示范围,而是具体的值)
  • between ... and...(between后面的数字需要比and后面的数字小,表示的能显示的数据的范围)
  • is null(为null才满足条件)
  • not(not可以取非,主要用在is和in中)
  • like(模糊查询,%代表多个字符,_代表单个字符)

排序

select math from student order by math;—>默认为升序排序

select math from student order by math desc;—>降序排序

select math from student order by math asc;—>升序排序

单行处理函数

select lower(name) from student;—>将name中的字母全部转换为小写字母

select upper(name) from student;—>将name中的字母全部转换为大写字母

select substr(name,1,1) from student;—>截取子字符串,第二个参数表示起始位置在MySql中是从1开始的,不是从0开始第三个参数是子字符串长度

select concat(str1,str2) from student;—>将str1,str2拼接成一个字符串

select length(name) from student;—>得到长度

select name from student where name = trim(“ 张飞”);—>trim将空格去掉

select round(123.1) from student;—>可以字节显示字面量(直接就是值可以是数字也可以是字符串,行数和表的行数相同),这里的round()函数是四舍五入函数

select round(123.45,1) form student;—>默认第二个参数为0,为1表示保留一位小数…如果是-1的话结果就是:120

select rand() from student;—>只是借助字面量输出,输出的是0-1的随机数

分组处理函数

select sum(math) from student;—>显示student表中math字段对应数据的总和
select count(math) from student;—>显示student表中math字段对应的不位null的数据的个数
select count() from student;—>表示所有字段显示student表中的行数,因为不可能有一行全部字段都为空,注意和上一行有个区分

select max(math) from student;—>显示student表中math字段对应数据中的最大值
select main(math) from student;—>显示student表中math字段对应数据中的最小值
select avg(math) from student;—>显示student表中math字段对应数据中的平均值
select math,avg(math) from student group by math;—>按math字段来分组(math字段中数据相同的的行为一组),并且排序出其平均值
select math,avg(math) from student group by math order by desc;—>分组使用显示,并且控制降序显示
select math,avg(math) from student group by math having avg(math) > 60;—>因为是会先执行where所有不能使用where这个时候就可以使用having来筛选结果了
分组函数可以单独使用,但是最好和group by一起连用

去重命令

dinstinct 命令(去重)
select count(dinstinct math) from student;

乐观锁和悲观锁

乐观锁相当于没有synchronized
悲观锁相当于有synchronized

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
行级锁(又叫悲观锁):
select ename,job,sal from emp where job = 'MANAGER';
+-------+---------+---------+
| ename | job | sal |
+-------+---------+---------+
| JONES | MANAGER | 3272.50 |
| BLAKE | MANAGER | 3135.00 |
| CLARK | MANAGER | 2695.00 |
+-------+---------+---------+

select ename,job,sal from emp where job = 'MANAGER' for update;
select语句后加 for update 就是行级锁
代表在当前事务结束之前,其他事务都无法修改这一行数据
上面select语句表示:那三行数据(其实是对应emp表中的三行数据)都无法修改

乐观锁:
多线程并发 都可以对这行数据修改

分组查询

连接查询?

内连接

等值连接

92语法:

1
2
3
4
5
6
select 
emp.ename, dept.dname
from
emp, dept
where
emp.deptno = dept.deptno;//筛选条件,没有就会出现笛卡尔积现象(这样的等值筛选条件就是等值查询),像类一样调用(吧表看成对象)

没有查询限制->笛卡尔积现象
不是为了调高效率只是为了避免笛卡尔积现象,次数是不会少的
上面语法的提高效率写法(给表起个别名)

1
2
3
4
5
6
select 
e.ename,d.dname
from
emp e, dept d
where
e.deptno = d.deptno; //SQL92语法

SQL92的缺点:结构不清晰,表的连接条件和后期进一步筛选的条件,都放到了where后面。
SQL99语法:

1
2
3
4
5
6
7
8
select 
e.ename,d.dname
from
emp e
join
dept d
on
e.deptno = d.deptno;

也可这样写:

1
2
3
4
5
6
7
8
select 
e.ename,d.dname
from
emp e
inner join //inner可以省略(带着inner可读性更好!!!一眼就能看出来是内连接)
dept d
on
e.deptno = d.deptno; // 条件是等量关系,所以被称为等值连接。

SQL99的优点:表连接的条件是独立的,连接之后,如果还需要进一步筛选,再往后继续添加where,可如下操作:

1
2
3
4
5
6
7
8
9
10
select 
...
from
a
join
b
on
a和b的连接条件
where
筛选条件

非等值连接

非等值连接: 条件不是一个等量关系。

1
2
3
4
5
6
7
8
select 
e.ename, e.sal, s.grade
from
emp e
join
salgrade s
on
e.sal between s.losal and s.hisal; // 条件不是一个等量关系,称为非等值连接。

自连接

(把自己看成两个表连接到一起)

1
2
3
4
5
6
7
8
select 
a.ename as '员工名', b.ename as '领导名'
from
emp a
join
emp b
on
a.mgr = b.empno; //员工的领导编号 = 领导的员工编号

外连接

有不平等关系,内连接是两个表都是同等关系.
偏向谁(left左边的表或right右边的表)谁就是主,是扫描这个主表,顺便去扫描另外一个表

左外连接

1
2
3
4
5
6
7
8
select 
e.ename,d.dname
from
dept d
left join
emp e
on
e.deptno = d.deptno;

右外连接

1
2
3
4
5
6
7
8
select 
e.ename,d.dname
from
emp e
right join
dept d
on
e.deptno = d.deptno;

outer 和 inner一样,也可省略

1
2
3
4
5
6
7
8
9
// outer是可以省略的,带着可读性强。
select
e.ename,d.dname
from
emp e
right outer join
dept d
on
e.deptno = d.deptno;

全连接(不讲)

多张表查询

1
2
3
4
5
6
7
8
9
10
11
12
select 
e.ename,e.sal,d.dname,s.grade
from
emp e
join
dept d
on
e.deptno = d.deptno // e表和d表的连接条件:e表的员工部门编号要等于d表的部门编号
join
salgrade s
on
e.sal between s.losal and s.hisal; // e表和s表的连接条件:e表的员工薪资要在s表的losal值和hisal值之间

子查询

子查询就是嵌套的 select 语句,可以理解为子查询是一张表。
select语句中嵌套select语句,被嵌套的select语句称为子查询。

可出现位置

1
2
3
4
5
6
select
..(select).
from
..(select).
where
..(select)

where

找出比最低工资高的员工姓名和工资:
select ename,sal from emp where sal > min(sal);(报错)
上述语句报错的原因:where子句中不能直接使用分组函数。
实现思路:
第一步:查询最低工资是多少 select min(sal) from emp;?
第二步:找出>800的 select ename,sal from emp where sal > 800;
第三步:合并 select ename,sal from emp where sal > (select min(sal) from emp);

from

在 from 语句中使用子查询,可以将该子查询的查询结果看做一张表。
找出每个岗位的平均工资的薪资等级
第一步:找出每个岗位的平均工资(按照岗位分组求平均值)。
????select job,avg(sal) from emp group by job;
第二步:克服心理障碍,把以上的查询结果就当做一张真实存在的表 t。
????select * from salgrade;得到s表
第三步:t表和s表进行表连接,条件:t表的avg(sal) between s.losal and s.hisal;
最后:

1
2
3
4
5
6
7
8
select 
t.*, s.grade
from
(select job,avg(sal) as avgsal from emp group by job) t
join
salgrade s
on
t.avgsal between s.losal and s.hisal;

select

1
2
3
4
select 
e.ename,(select d.dname from dept d where e.deptno = d.deptno) as dname
from
emp e;

注意:对于select后面的子查询来说,这个子查询只能一次返回1条结果,多于1条,就报错了。

union

union合并查询结果集。
union 可以合并集合(相加)。

1
2
3
select ename,job from emp where job = 'MANAGER'
union
select ename,job from emp where job = 'SALESMAN';

union的效率要高一些。对于表连接来说,每连接一次新表,则匹配的次数满足笛卡尔积,成倍的翻。但是union可以减少匹配的次数。在减少匹配次数的情况下,还可以完成两个结果集的拼接。
a 连接 b 连接 c
a 10条记录
b 10条记录
c 10条记录
匹配次数是:1000
a 连接 b 一个结果:10 10 —> 100次
a 连接 c 一个结果:10
10 —> 100次
使用union的话是:100次 + 100次 = 200次。(union把乘法变成了加法运算)

union在使用的时候的注意事项:

  1. union在进行结果集合并的时候,要求两个结果集的列数相同。
  2. MySQL可以,oracle语法严格 ,不可以,报错。要求:结果集合并时列和列的数据类型也要一致。

limit

imit 的作用

MySQL 提供了 limit,主要用于提取前几条或者中间某几行数据。例如,select from table limit m,n,其中 m 是指记录开始的 index,从 0 开始,表示第一条记录;n 是指从第 m+1 条开始,取 n 条。select from tablename limit 2,4即取出第 3 条至第 6 条,4 条记录。
??limit 的作用:将查询结果集的一部分取出来,通常使用在分页查询当中。分页的作用是为了提高用户的体验,因为一次全部都查出来,用户体验差。

limit 的用法

完整用法:limit startIndex, length
startIndex是起始下标,length是长度。
起始下标从0开始。
缺省用法:limit 5; 这是取前5。

注意:mysql当中limit在order by之后执行!

每页显示3条记录
第1页:limit 0,3 [0 1 2]
第2页:limit 3,3 [3 4 5]
第3页:limit 6,3 [6 7 8]
第4页:limit 9,3 [9 10 11]

每页显示pageSize条记录
第pageNo页:limit (pageNo - 1) * pageSize , pageSize

执行顺序:

  1. from
  2. where
  3. group by
  4. having
  5. select
  6. order by
  7. limit

建表的语法格式:(建表属于DDL语句,DDL包括:create drop alter)

表的创建

语法:
create table 表名(字段名1 数据类型, 字段名2 数据类型, 字段名3 数据类型);
或者:

1
2
3
4
5
create table 表名(
字段名1 数据类型,
字段名2 数据类型,
字段名3 数据类型
);

数据类型

varchar

varchar(最长255):可变长度的字符串。
比较智能,节省空间。
会根据实际的数据长度动态分配空间。
优点: 节省空间。
缺点: 需要动态分配空间,速度慢。

char

char(最长255):定长字符串。
不管实际的数据长度是多少,分配固定长度的空间去存储数据。
使用不恰当的时候,可能会导致空间的浪费。

优点: 不需要动态分配空间,速度快。
缺点: 使用不当可能会导致空间的浪费。

varchar和char应该怎么选择?
性别字段选什么?因为性别是固定长度的字符串,所以选择char。
姓名字段选什么?每一个人的名字长度不同,所以选择varchar。

int

int(最长11):数字中的整数型。等同于java的int。

bigint

bigint:数字中的长整型。等同于java中的long。

float

float:单精度浮点型数据。

double

double:双精度浮点型数据。

date

date:短日期类型。

datetime

datetime:长日期类型。

clob

clob:字符大对象(Character Large OBject)。
最多可以存储4G的字符串。比如:存储一篇文章,存储一个说明。
超过255个字符的都要采用CLOB字符大对象来存储。

blob

blob:二进制大对象(Binary Large OBject)。
专门用来存储图片、声音、视频等流媒体数据。
往BLOB类型的字段上插入数据的时候,例如插入一个图片、视频等,需要使用IO流才行。

示例

创建一个学生表:
学号、姓名、年龄、性别、邮箱地址

1
2
3
4
5
6
7
create table t_student(
no int,
name varchar(32),
sex char(1),
age int(3),
email varchar(255)
);

表的删除

删除表:drop table t_student;当这张表不存在的时候会报错!
??如果这张表存在的话,删除:drop table if exists t_student;

插入数据 insert (DML)

  • 语法格式: insert into 表名(字段名1,字段名2,字段名3...) values(值1,值2,值3);

    注意:字段名和值要一一对应。什么是一一对应数量要对应,数据类型要对应。

例如:
插入no为”1”、name为”zhangsan”、sex为”m”、age为”20”、email为”zhangsan@123.com”的数据:insert into t_student(no,name,sex,age,email) values(1,'zhangsan','m',20,'zhangsan@123.com');

注意:

  1. insert语句但凡是执行成功了,那么必然会多一条记录。
  2. 没有给其它字段指定值的话,默认值是NULL。

创建新表,设置sex的默认值为”m”。

1
2
3
4
5
6
7
create table t_student(
no int,
name varchar(32),
sex char(1) default 'm',
age int(3),
email varchar(255)
);

省略字段

insert语句中的“字段名”可以省略吗?可以。
??但是insert into t_student values(2); 是错误的,因为前面的字段名省略的话,等于都写上了,所以值也要都写上

insert 插入日期

数字格式化:format
从员工信息表中查询员工名和工资:select ename,sal from emp;
格式化数字:format(数字, '格式')
select ename,format(sal, '$999,999') as sal from emp;

str_to_date():将字符串varchar类型转换成date类型。
date_format():将date类型转换成具有一定格式的varchar字符串类型。
在MySQL当中怎么获取系统当前时间?
now()函数,并且获取的时间带有时分秒信息,是datetime类型的。例如,insert into t_user(id,name,birth,create_time) values(2,'lisi','1991-10-01',now());

可以使用str_to_date函数进行类型转换。str_to_date函数可以将字符串varchar转换成日期类型date,语法格式:str_to_date('字符串日期', '日期格式')


MySQL的日期格式:
%Y 年
%m 月
%d 日
%h 时
%i 分
%s 秒

示例:
插入id为1,name为”zhangsan”,birth为1990年10月1日的数据语句改为:insert into t_user(id,name,birth) values(1, 'zhangsan', str_to_date('01-10-1990','%d-%m-%Y'));

date_format这个函数可以将日期类型转换成特定格式的字符串。
date_format函数的用法:date_format(日期类型数据, '日期格式'),这个函数通常使用在查询日期方面,设置展示的日期格式。例如,select id,name,date_format(birth, '%m/%d/%Y') as birth from t_user;

date 和 datetime 两个类型的区别

date是短日期:只包括年月日信息。
datetime是长日期:包括年月日时分秒信息。

MySQL短日期默认格式:%Y-%m-%d。
MySQL长日期默认格式:%Y-%m-%d %h:%i:%s

修改 update(DML)

语法格式:update 表名 set 字段名1=值1,字段名2=值2,字段名3=值3... where 条件;
注意 set
注意: 没有条件限制会导致所有数据全部更新。

更新id为2的name、birth信息:
update t_user set name = 'jack', birth = '2000-10-11' where id = 2;
更新id为2的create_time信息:
update t_user set name = 'jack', birth = '2000-10-11', create_time = now() where id = 2;
更新所有:
update t_user set name = 'abc';

删除数据 delete (DML)

语法格式:delete from 表名 where 条件;
注意: 没有条件,整张表的数据会全部删除!

删除表中id为2的信息:
delete from t_user where id = 2;
插入id为2的数据:
insert into t_user(id) values(2);
删除所有:
delete from t_user;

表的一些比较高级的操作

insert 语句一次插入多条记录

insert 语句一次插入多条记录的语法:insert into t_user(字段名1,字段名2) values(),(),(),();
示例:

1
2
3
4
insert into t_user(id,name,birth,create_time) values
(1,'zs','1980-10-11',now()),
(2,'lisi','1981-10-11',now()),
(3,'wangwu','1982-10-11',now());

快速创建表

快速创建表emp2:create table emp2 as select * from emp;

原理:

  1. 将一个查询结果当做一张表新建!
  2. 这个可以完成表的快速复制!
  3. 表创建出来,同时表中的数据也存在了!

有条件的创建表mytable(只复制一部分新建):
create table mytable as select empno,ename from emp where job = 'MANAGER';

将查询结果插入到一张表当中

?快速创建表 dept_bak: create table dept_bak as select * from dept;
将查询结果插入到表 dept_bak 当中:insert into dept_bak select * from dept;(很少用)

快速删除表中的数据(较重要)

删除 dept_bak 表中的数据:delete from dept_bak;,这种删除数据的方式比较慢。
delete语句删除数据的原理:(delete属于DML语句!)
表中的数据被删除了,但是这个数据在硬盘上的真实存储空间不会被释放!
这种删除缺点是:删除效率比较低。
这种删除优点是:支持回滚,后悔了可以再恢复数据!

truncate语句删除数据的原理:
这种删除效率比较高,表被一次截断,物理删除。
这种删除缺点:不支持回滚。
这种删除优点:快速。

truncate语句删除数据的用法:truncate table dept_bak;(这种操作属于DDL操作)

当大表非常大,例如上亿条记录,删除的时候,使用delete,也许需要执行1个小时才能删除完,效率较低。可以选择使用truncate删除表中的数据,只需要不到1秒钟的时间就删除结束,效率较高。但是使用truncate之前,必须仔细询问客户是否真的要删除,并警告删除之后不可恢复!

truncate是删除表中的数据,表还在!
删除表操作:drop table 表名;这不是删除表中的数据,这是把表删除。

结构的增删改(不太重要)

什么是对表结构的修改?
??添加一个字段,删除一个字段,修改一个字段!

??对表结构的修改需要使用:alter(属于DDL语句,DDL包括:create 、drop 、alter。)

注意:

  1. 在实际的开发中,需求一旦确定之后,表一旦设计好之后,很少的进行表结构的修改。因为开发进行中的时候,修改表结构,成本比较高。
  2. 由于修改表结构的操作很少,所以我们不需要掌握,如果有一天真的要修改表结构,可以使用工具!

创建表加入约束

什么是约束

约束对应的英语单词:constraint。
在创建表的时候,我们可以给表中的字段加上一些约束,来保证这个表中数据的完整性、有效性!
约束的作用就是为了保证表中的数据有效!

约束包括哪些

非空约束:not null
唯一性约束:unique
主键约束: primary key (简称PK)
外键约束:foreign key(简称FK)
检查约束:check(mysql不支持,oracle支持)

非空约束 not null

非空约束not null:约束的字段不能为NULL。
示例:(创建一个表 t_vip)

1
2
3
4
5
drop table if exists t_vip;
create table t_vip(
id int,
name varchar(255) not null
);

not null只有列级约束,没有表级约束!
上面建表约束后:当只插入id信息,name信息为NULL时,会报错.

注意:

  1. xxxx.sql 这种文件被称为sql脚本文件。
  2. sql 脚本文件中编写了大量的 sql 语句。我们执行sql脚本文件的时候,该文件中所有的 sql 语句会全部执行!
  3. 批量的执行 sql 语句,可以使用 sql 脚本文件。
  4. 在mysql当中执行sql脚本:source E:\工具+源码+资料\document\vip.sql

唯一性约束 unique

唯一性约束 unique :约束的字段不能重复,但是可以为NULL

1
2
3
4
5
6
7
8
9
10
drop table if exists t_vip;
create table t_vip(
id int,
name varchar(255) unique,
email varchar(255)
);
insert into t_vip(id,name,email) values(1,'zhangsan','zhangsan@123.com');
insert into t_vip(id,name,email) values(2,'lisi','lisi@123.com');
insert into t_vip(id,name,email) values(3,'wangwu','wangwu@123.com');
select * from t_vip;

当再插入的数据与之前的有重复时,会报错!
name字段虽然被unique约束了,但是可以为NULL。
name varchar(255) unique,约束直接添加到列后面的,叫做列级约束
不使用列级约束:unique(name,email),约束没有添加在列的后面,这种约束被称为表级约束

1
2
3
4
5
6
7
8
9
10
drop table if exists t_vip;
create table t_vip(
id int,
name varchar(255),
email varchar(255),
unique(name,email)
);
insert into t_vip(id,name,email) values(1,'zhangsan','zhangsan@123.com');
insert into t_vip(id,name,email) values(2,'zhangsan','zhangsan@sina.com');
select * from t_vip;

注意,这样不是这两个有一个重复都会报错,而是当两个同时重复才会报错
uniquenot null可以联合:

1
2
3
4
5
6
drop table if exists t_vip;
create table t_vip(
id int,
name varchar(255) not null unique
);
desc t_vip;

在mysql当中,如果一个字段同时被not null和unique约束的话,该字段自动变成主键字段

主键约束(primary key)(非常非常重要)

主键约束: primary key,简称PK。

主键约束的相关术语:

主键约束: 就是一种约束。
主键字段: 该字段上添加了主键约束,这样的字段叫做主键字段
主键值: 主键字段中的每一个值都叫做主键值。

主键

??每个表应该具有主键,主键可以标识记录的唯一性,主键分为单一主键和复合(联合)主键,单一主键是由一个字段构成的,复合(联合)主键是由多个字段构成的。

?什么是主键?有什么作用?
??主键值是每一行记录的唯一标识。主键值是每一行记录的身份证号(如果其他字段的值都相同,要求要有一个字段的值能够来区分)!
??任何一张表都应该有主键,没有主键,表无效!(相当于人没有身份证号)
??主键的特征:not null + unique(主键值不能是NULL,同时也不能重复!)

示例(创建一个有主键的表):

1
2
3
4
5
6
7
drop table if exists t_vip;
create table t_vip(
id int primary key,
name varchar(255)
);
insert into t_vip(id,name) values(1,'zhangsan');
insert into t_vip(id,name) values(2,'lisi');

上述语句中,id int primary key列级约束id做主键:单一主键

1
2
3
4
5
6
7
drop table if exists t_vip;
create table t_vip(
id int,
name varchar(255),
primary key(id)
);
insert into t_vip(id,name) values(1,'zhangsan');

其中,primary key(id)表级约束idname联合起来做主键:复合主键
(在实际开发中不建议使用复合主键,建议使用单一主键!因为主键值存在的意义就是这行记录的身份证号,只要意义达到即可,单一主键可以做到。而复合主键比较复杂,不建议使用。)

一张表,主键约束只能添加1个(主键只能有1个)。

主键值

主键值建议使用int、bigint、char等类型,不建议使用varchar来做主键。主键值一般都是数字,一般都是定长的。

主键分类:
单一主键和复合主键
???单一主键:由一个字段构成。
???复合主键: 由多个字段构成。
自然主键和业务主键
???自然主键:主键值是一个自然数,和业务没关系
???业务主键:主键值和业务紧密关联,例如拿银行卡账号做主键值。

??在实际开发中使用自然主键使用比较多,因为主键只要做到不重复就行,不需要有意义。业务主键不好,因为主键一旦和业务挂钩,那么当业务发生变动的时候,可能会影响到主键值,所以业务主键不建议使用,尽量使用自然主键。

在mysql当中,有一种机制,可以帮助我们自动维护一个主键值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
drop table if exists t_vip;
create table t_vip(
id int primary key auto_increment,
name varchar(255)
);
insert into t_vip(name) values('zhangsan');
insert into t_vip(name) values('zhangsan');
insert into t_vip(name) values('zhangsan');
insert into t_vip(name) values('zhangsan');
insert into t_vip(name) values('zhangsan');
insert into t_vip(name) values('zhangsan');
insert into t_vip(name) values('zhangsan');
insert into t_vip(name) values('zhangsan');
select * from t_vip;

其中,id int primary key auto_increment语句中auto_increment表示自增,从1开始,以1递增。auto_increment
使用了auto_increment之后如果不给id赋值id会自增,但是任然还是可以赋值,只是不能和前面重复了

外键约束(foreign key)(非常非常重要)

外键约束: foreign key,简称FK。

外键约束的相关术语:

???外键约束: 一种约束(foreign key)。
???外键字段: 该字段上添加了外键约束。
???外键值: 外键字段当中的每一个值。

??外键主要是维护表之间的关系的,主要是为了保证参照完整性,如果表中的某个字段为外键字段,那么该字段的值必
须来源于参照的表的主键,如:emp 中的 deptno 值必须来源于 dept 表中的 deptno 字段值。

示例

业务背景: 请设计数据库表,来描述“班级和学生”的信息。

第一种方案:班级和学生存储在一张表中
t_student
1
2
3
4
5
6
7
8
9
10
no(pk)			name		classno			classname
----------------------------------------------------------------------------------
1 jack 100 北京市大兴区亦庄镇第二中学高三1班
2 lucy 100 北京市大兴区亦庄镇第二中学高三1班
3 lilei 100 北京市大兴区亦庄镇第二中学高三1班
4 hanmeimei 100 北京市大兴区亦庄镇第二中学高三1班
5 zhangsan 101 北京市大兴区亦庄镇第二中学高三2班
6 lisi 101 北京市大兴区亦庄镇第二中学高三2班
7 wangwu 101 北京市大兴区亦庄镇第二中学高三2班
8 zhaoliu 101 北京市大兴区亦庄镇第二中学高三2班

以上方案的缺点:数据冗余,空间浪费!

第二种方案:班级一张表、学生一张表

t_class 班级表

1
2
3
4
classno(pk)			classname
------------------------------------------------------
100 北京市大兴区亦庄镇第二中学高三1班
101 北京市大兴区亦庄镇第二中学高三1班

t_student 学生表

1
2
3
4
5
6
7
8
9
10
no(pk)			name				cno(FK引用t_class这张表的classno)
----------------------------------------------------------------
1 jack 100
2 lucy 100
3 lilei 100
4 hanmeimei 100
5 zhangsan 101
6 lisi 101
7 wangwu 101
8 zhaoliu 101

当cno字段没有任何约束的时候,可能会导致数据无效。可能出现一个102,但是102班级不存在。所以为了保证cno字段中的值都是100和101,需要给cno字段添加外键约束,那么cno字段就是外键字段,cno字段中的每一个值都是外键值。

实现:

首先建立班级表 t_classes:

1
2
3
4
5
6
drop table if exists t_classes;
create table t_classes(
classes_id int(3),
classes_name varchar(40),
constraint pk_classes_id primary key(classes_id)
);

在 t_student 中加入外键约束

1
2
3
4
5
6
7
8
9
10
11
drop table if exists t_student;
create table t_student(
student_id int(10),
student_name varchar(20),
sex char(2),
birthday date,
email varchar(30),
classes_id int(3),
constraint student_id_pk primary key(student_id),//主键
constraint fk_classes_id foreign key(classes_id) references t_classes(classes_id) //外键
);

constraint的意思是约束
references的意思是引用
constraint fk_classes_id foreign key(classes_id) references t_classes(classes_id)的意思是:受约束的fk_classes_id受外键(t_classes_id(在t_classes表中))约束,引用t_classes表中的class_id字段意思是在t_student表中的fk_classes_id的值只能是t_classes中classes_id中的值

注意:
t_class 是父表
t_student是子表
删除表的顺序:先删子,再删父。
创建表的顺序:先创建父,再创建子。
删除数据的顺序:先删子,再删父。
插入数据的顺序:先插入父,再插入子。

外键不一定是父表的主键,单=但至少要有unique约束(外键可以为null)

存储引擎(了解)

事务(非常非常重要)

什么是事务

一个事务其实就是一个完整的业务逻辑,是一个最小的工作单元,不可再分。

什么是一个完整的业务逻辑:
假设转账,从A账户向B账户中转账10000,
将A账户的钱减去10000(update语句),
将B账户的钱加上10000(update语句),
这就是一个完整的业务逻辑。

以上的操作是一个最小的工作单元,要么同时成功,要么同时失败,不可再分。这两个update语句要求必须同时成功或者同时失败,这样才能保证钱是正确的。

与事务相关的语句

只有DML语句才会有事务这一说,其它语句和事务无关。因此,只有insertdeleteupdate这三个语句和事务有关系,其它都没有关系。因为只有以上的三个语句是数据库表中数据进行增、删、改的。
??只要操作一旦涉及到数据的增、删、改,那么就一定要考虑安全问题,数据安全第一位!

事务机制的存在价值

??假设所有的业务,只要一条DML语句就能完成,还有必要存在事务机制吗?
??正是因为做某件事的时候,需要多条DML语句共同联合起来才能完成,所以需要事务的存在。如果任何一件复杂的事儿都能一条DML语句搞定,那么事务则没有存在的价值了。

到底什么是事务呢?
本质上,一个事务其实就是多条DML语句同时成功,或者同时失败
事务: 就是批量的DML语句同时成功,或者同时失败!

事务的执行过程

事务是怎么做到多条DML语句同时成功和同时失败的呢?
InnoDB存储引擎提供一组用来记录事务性活动的日志文件。

在事务的执行过程中,每一条DML的操作都会记录到“事务性活动的日志文件”中。

在事务的执行过程中,我们可以提交事务,也可以回滚事务。
提交事务
清空事务性活动的日志文件,将数据全部彻底持久化到数据库表中。提交事务标志着事务的结束,并且是一种全部成功的结束

回滚事务
将之前所有的DML操作全部撤销,并且清空事务性活动的日志文件。回滚事务标志着事务的结束,并且是一种全部失败的结束

提交事务和回滚事务

提交事务:commit; 语句
回滚事务:rollback; 语句(回滚永远都是只能回滚到上一次的提交点

事务对应的英语单词是:transaction

mysql默认情况下是支持自动提交事务的。(自动提交)
自动提交:每执行一条DML语句,则提交一次!

怎么将mysql的自动提交机制关闭掉呢?

先执行这个命令:`start transaction;

回滚事务

先执行命令start transaction;将自动提交机制关闭,往空表 dept_bak1 中添加三条数据后,并没有提交,所以执行回滚命令之后,dept_bak1 会退回到空表状态。

提交事务

先执行命令start transaction;将自动提交机制关闭,往表 dept_bak1 中再添加两条条数据后,执行commit命令进行提交,所以执行回滚命令之后,dept_bak1 中数据依然存在。

事务的4个特性

  1. 原子性(A): 说明事务是最小的工作单元,不可再分。
  2. 一致性(C): 所有事务要求,在同一个事务当中,所有操作必须同时成功,或者同时失败,以保证数据的一致性。
  3. 隔离性(I):看后面
  4. 持久性(D):事务最终结束的一个保障。事务提交,就相当于将没有保存到硬盘上的数据保存到硬盘上!

事务的隔离性

??A教室和B教室中间有一道墙,这道墙可以很厚,也可以很薄,这就是事务的隔离级别。这道墙越厚,表示隔离级别就越高。

事务和事务之间的隔离级别有4个级别:

  1. 读未提交: read uncommitted(最低的隔离级别),即没有提交就读到。
    什么是读未提交?->事务A可以读取到事务B未提交的数据。
    这种隔离级别存在的问题就是:脏读现象(Dirty Read)!我们称读到了脏数据。
    这种隔离级别一般都是理论上的,大多数的数据库隔离级别都是二档起步!(比这个高)
  2. 读已提交: read committed,即提交之后才能读到。
    什么是读已提交?->事务A只能读取到事务B提交之后的数据。
    这种隔离级别解决了什么问题?->解决了脏读的现象。
    这种隔离级别存在什么问题?->不可重复读取数据。
    什么是不可重复读取数据呢?->在事务开启之后,第一次读到的数据是3条,当前事务还没有结束,可能第二次再读取的时候,读到的数据是4条,3不等于4称为不可重复读取。
    这种隔离级别是比较真实的数据,每一次读到的数据是绝对的真实
    oracle数据库默认的隔离级别是:read committed。
  3. 可重复读: repeatable read,即提交之后也读不到,永远读取的都是刚开启事务时的数据。
    什么是可重复读取?->事务A开启之后,不管是多久,每一次在事务A中读取到的数据都是一致的。即使事务B将数据已经修改,并且提交了,事务A读取到的数据还是没有发生改变,这就是可重复读。
    可重复读解决了什么问题?解决了不可重复读取数据。
    可重复读存在的问题是什么?可能会出现幻影读。每一次读取到的数据都是幻象,不够真实!早晨9点开始开启了事务,只要事务不结束,到晚上9点,读到的数据还是那样!读到的是假象,不够绝对的真实。
    mysql中默认的事务隔离级别就是这个!
  4. 序列化/串行化: serializable(最高的隔离级别),这是最高隔离级别,效率最低。
    解决了所有的问题。
    这种隔离级别表示事务排队,不能并发!
    synchronized,线程同步(事务同步)
    每一次读取到的数据都是最真实的,并且效率是最低的。
验证:read uncommited

set global transaction isolation level read uncommitted;

验证:read commited

set global transaction isolation level read committed;

验证:repeatable read

set global transaction isolation level repeatable read;

验证:serializable

set global transaction isolation level serializable;

索引(index)

什么是索引

?索引(index)是在数据库表的字段上添加的,是为了提高查询效率存在的一种机制。一张表的一个字段可以添加一个索引,当然,多个字段联合起来也可以添加索引。索引相当于一本书的目录,是为了缩小扫描范围而存在的一种机制。

对于一本字典来说,查找某个汉字有两种方式:

  1. 一页一页挨着找,直到找到为止,这种查找方式属于全字典扫描,效率比较低。
  2. 先通过目录(索引)去定位一个大概的位置,然后直接定位到这个位置,做局域性扫描,缩小扫描的范围,快速的查找。这种查找方式属于通过索引检索,效率较高。
    MySQL在查询方面主要就是两种方式:
    第一种方式:全表扫描。
    第二种方式:根据索引检索。

注意:
在实际中,汉语字典前面的目录是排序的,按照a b c d e f…排序。为什么排序呢?因为只有排序了才会有区间查找这一说!(缩小扫描范围,其实就是扫描某个区间!)
??在mysql数据库当中索引也是需要排序的,并且这个索引的排序和TreeSet数据结构相同。TreeSet(TreeMap)底层是一个自平衡的二叉树!
??在mysql当中,索引是一个B-Tree数据结构。遵循左小又大原则存放,采用中序遍历方式遍历取数据。

索引的实现原理

注意:

  1. 在任何数据库当中,主键上都会自动添加索引对象,id字段上自动有索引,因为id是PK。另外在mysql当中,一个字段上如果有unique约束的话,也会自动创建索引对象
  2. 在任何数据库当中,任何一张表的任何一条记录在硬盘存储上都有一个硬盘的物理存储编号
  3. 在mysql当中,索引是一个单独的对象,不同的存储引擎以不同的形式存在,在MyISAM存储引擎中,索引存储在一个.MYI文件中。在InnoDB存储引擎中,索引存储在一个逻辑名称叫做tablespace的当中。在MEMORY存储引擎当中,索引被存储在内存当中。不管索引存储在哪里,索引在mysql当中都是一个树的形式存在。(自平衡二叉树:B-Tree)

表中字段不会动,是对对象地址排序

添加索引的条件

在mysql当中,主键上以及unique字段上都会自动添加索引的。
什么条件下,我们会考虑给字段添加索引呢?
条件1:数据量庞大。(到底有多么庞大算庞大,这个需要测试,因为每一个硬件环境不同。)
条件2:该字段经常出现在where的后面,以条件的形式存在,也就是说这个字段总是被扫描。
条件3:该字段很少的DML(insert、delete、update)操作。(因为DML之后,索引需要重新排序。)

建议不要随意添加索引,因为索引也是需要维护的,太多的话反而会降低系统的性能。
建议通过主键查询,建议通过unique约束的字段进行查询,效率是比较高的。

索引的创建和删除

创建索引: create index emp_ename_index on emp(ename);
给emp表的ename字段添加索引,起名为emp_ename_index

删除索引: drop index emp_ename_index on emp;
将emp表上的emp_ename_index索引对象删除。

如何查看SQL语句是否使用索引进行检索

在mysql当中,怎么查看一个SQL语句是否使用了索引进行检索?
explain select * from emp where ename = 'KING';
扫描14条记录:说明没有使用索引(type=ALL)。

索引失效的情况

失效的第1种情况: 使用模糊查询的时候,条件以“%”开始。

select * from emp where ename like '%T';
ename上即使添加了索引,也不会走索引,为什么?原因是因为模糊匹配当中以“%”开头了! 尽量避免模糊查询的时候以“%”开始。这是一种优化的手段/策略。

失效的第2种情况: 使用or的时候会失效

如果使用or,那么要求or两边的条件字段都要有索引,才会走索引。如果其中一边有一个字段没有索引,那么另一个字段上的索引也会失效,所以这就是为什么不建议使用or的原因

失效的第3种情况: 使用复合索引的时候

没有使用左侧的列查找,则索引失效。什么是复合索引?
两个字段,或者更多的字段联合起来添加一个索引,叫做复合索引。
创建复合索引:create index emp_job_sal_index on emp(job,sal);
使用左侧的列查找:explain select * from emp where job = 'MANAGER';,索引未失效。
没有使用左侧的列查找:explain select * from emp where sal = 800;,索引失效。

失效的第4种情况: 在where当中索引列参加了运算的时候

创建索引:create index emp_sal_index on emp(sal);
在where当中索引列为未进行运算:explain select * from emp where sal = 800;,索引未失效。
在where当中索引列进行了运算:explain select * from emp where sal+1 = 800;,索引失效。

失效的第5种情况: 在where当中索引使用了函数

explain select * from emp where lower(ename) = ‘smith’;

索引的分类

??索引是各种数据库进行优化的重要手段,优化的时候优先考虑的因素就是索引。

?索引在数据库当中的分类:

单一索引: 一个字段上添加索引。
复合索引: 两个字段或者更多的字段上添加索引。
主键索引: 主键上添加索引。
唯一性索引: 具有unique约束的字段上添加索引。

视图

什么是视图

视图(view):站在不同的角度去看待同一份数据。

创建和删除视图对象

创建视图对象:create view dept2_view as select * from dept2;

删除视图对象:drop view dept2_view;

注意:只有DQL语句才能以view的形式创建。create view view_name as 这里的语句必须是DQL语句

视图的作用

我们可以面向视图对象进行增删改查,对视图对象的增删改查,会导致原表被操作!(视图的特点: 通过对视图的操作,会影响到原表数据。)

面向视图查询:select * from dept2_view;
查询原表数据:select * from dept2;
查询视图对象:select * from emp_dept_view;

面向视图插入:insert into dept2_view(deptno,dname,loc) values(60,'SALES', 'BEIJING');

面向视图删除:delete from dept2_view;

面向视图更新:update emp_dept_view set sal = 1000 where dname = 'ACCOUNTING';
原表数据被更新:select * from emp;

视图对象的作用

视图对象在实际开发中到底有什么用?
??方便,简化开发,利于维护。

假设有一条非常复杂的SQL语句,而这条SQL语句需要在不同的位置上反复使用。每一次使用这个SQL语句的时候都需要重新编写,很长,很麻烦,怎么办?
??可以把这条复杂的SQL语句以视图对象的形式新建。在需要编写这条SQL语句的位置直接使用视图对象,可以大大简化开发。并且利于后期的维护,因为修改的时候也只需要修改一个位置就行,只需要修改视图对象所映射的SQL语句。

??以后面向视图开发的时候,使用视图的时候可以像使用table一样,可以对视图进行增删改查等操作。视图不是在内存当中,视图对象也是存储在硬盘上的,不会消失。
??注意:视图对应的语句只能是DQL语句,但是视图对象创建完成之后,可以对视图进行增删改查等操作。

??增删改查,又叫做:CRUD。CRUD是在公司中程序员之间沟通的术语,一般我们很少说增删改查,一般都说CRUD。

C:Create(增)
R:Retrive(查:检索)
U:Update(改)
D:Delete(删)

DBA常用命令

只需要掌握:数据的导入和导出(数据的备份)。
其它命令了解一下即可。(使用其他用户之类的,到时候去查一查就好了)

数据导出`

mysqldump bjpowernode emp>D:\bjpowernode.sql -uroot -p123456

数据导入:

注意:需要先登录到mysql数据库服务器上。
然后创建数据库:create database bjpowernode;
使用数据库:use bjpowernode
然后初始化数据库:source D:\bjpowernode.sql

数据库设计三范式

数据库设计范式: 数据库表的设计依据,教我们怎么进行数据库表的设计。

第一范式: 要求任何一张表必须有主键,每一个字段原子性不可再分。
第二范式: 建立在第一范式的基础之上,要求所有非主键字段完全依赖主键,不要产生部分依赖。
第三范式: 建立在第二范式的基础之上,要求所有非主键字段直接依赖主键,不要产生传递依赖。