本教程适合有一定基础的人,我是用来复习mysql数据,跟着教程走一遍熟悉一下mysql的语句
数据准备
下面的数据库查询语句都是基于此表进行查询的
员工表
创建表:
-- 创建表
drop table if exists emp;
create table emp
(id int comment '编号',workno varchar(10) comment '工号',name varchar(10) comment '姓名',gender char(1) comment '性别',age tinyint unsigned comment '年龄',idcard char(18) comment '身份证号',workaddress varchar(50) comment '工作地址',entrydate date comment '入职时间'
) comment '员工表';
插入数据:
-- 插入数据
INSERT INTO emp (id, workno, name, gender, age, idcard, workaddress, entrydate)
VALUES (1, '00001', '柳岩666', '女', 20, '123456789012345678', '北京', '2000-01-01');
INSERT INTO emp (id, workno, name, gender, age, idcard, workaddress, entrydate)
VALUES (2, '00002', '张无忌', '男', 18, '123456789012345670', '北京', '2005-09-01');
INSERT INTO emp (id, workno, name, gender, age, idcard, workaddress, entrydate)
VALUES (3, '00003', '韦一笑', '男', 38, '123456789712345670', '上海', '2005-08-01');
INSERT INTO emp (id, workno, name, gender, age, idcard, workaddress, entrydate)
VALUES (4, '00004', '赵敏', '女', 18, '123456757123845670', '北京', '2009-12-01');
INSERT INTO emp (id, workno, name, gender, age, idcard, workaddress, entrydate)
VALUES (5, '00005', '小昭', '女', 16, '123456769012345678', '上海', '2007-07-01');
INSERT INTO emp (id, workno, name, gender, age, idcard, workaddress, entrydate)
VALUES (6, '00006', '杨逍', '男', 28, '12345678931234567X', '北京', '2006-01-01');
INSERT INTO emp (id, workno, name, gender, age, idcard, workaddress, entrydate)
VALUES (7, '00007', '范瑶', '男', 40, '123456789212345670', '北京', '2005-05-01');
INSERT INTO emp (id, workno, name, gender, age, idcard, workaddress, entrydate)
VALUES (8, '00008', '黛绮丝', '女', 38, '123456157123645670', '天津', '2015-05-01');
INSERT INTO emp (id, workno, name, gender, age, idcard, workaddress, entrydate)
VALUES (9, '00009', '范凉凉', '女', 45, '123156789012345678', '北京', '2010-04-01');
INSERT INTO emp (id, workno, name, gender, age, idcard, workaddress, entrydate)
VALUES (10, '00010', '陈友谅', '男', 53, '123456789012345670', '上海', '2011-01-01');
INSERT INTO emp (id, workno, name, gender, age, idcard, workaddress, entrydate)
VALUES (11, '00011', '张士诚', '男', 55, '123567897123465670', '江苏', '2015-05-01');
INSERT INTO emp (id, workno, name, gender, age, idcard, workaddress, entrydate)
VALUES (12, '00012', '常遇春', '男', 32, '123446757152345670', '北京', '2004-02-01');
INSERT INTO emp (id, workno, name, gender, age, idcard, workaddress, entrydate)
VALUES (13, '00013', '张三丰', '男', 88, '123656789012345678', '江苏', '2020-11-01');
INSERT INTO emp (id, workno, name, gender, age, idcard, workaddress, entrydate)
VALUES (14, '00014', '灭绝', '女', 65, '123456719012345670', '西安', '2019-05-01');
INSERT INTO emp (id, workno, name, gender, age, idcard, workaddress, entrydate)
VALUES (15, '00015', '胡青牛', '男', 70, '12345674971234567X', '西安', '2018-04-01');
INSERT INTO emp (id, workno, name, gender, age, idcard, workaddress, entrydate)
VALUES (16, '00016', '周芷若', '女', 18, null, '北京', '2012-06-01');
成绩表
创建表:
create table score
(id int comment 'ID',name varchar(20) comment '姓名',math int comment '数学',english int comment '英语',chinese int comment '语文'
) comment '学员成绩表';
插入数据:
insert into score(id, name, math, english, chinese)
VALUES (1, 'Tom', 67, 88, 95),(2, 'Rose', 23, 66, 90),(3, 'Jack', 56, 98, 76);
1.DDL 数据定义语言
1.1 数据库操作
-- 1.查询所有数据库
show
databases
-- 2.查询当前数据库
select database()
-- 3.创建数据库
create
database if not exists itcast
-- 创建带有字符集编码的数据库
create
database if not exists itheima DEFAULT CHARSEt utf8mb4
-- 4.删除数据库
drop
test
-- 5.使用数据库
use mysql
1.2 表查询
-- 1.查询当前数据库的所有表
show tables;-- 2.查询表结构
desc db-- 3.查询指定表的创建语句
show
create table db
1.3 表创建
在数据库优化中,能用char的尽量不使用varchar,因为varchar会浪费很多存储空间
-- 创建表
create table tb_user
(id int comment '编号',name varchar(50) comment '姓名',age int comment '年龄',gender varchar(1) comment '性别'
)comment '用户表'
根据需求创建一个表:
-- 设计一张员工信息表,要求如下:
-- 1.编号(纯数字)
-- 2.员工工号 (字符串类型,长度不超过10位)
-- 3. 员工姓名(字符串类型,长度不超过10位)
-- 4.性别(男/女,存储- -个汉字)
-- 5. 年龄(正常人年龄,不可能存储负数)
-- 6. 身份证号(二代身份证号均为18位,身份证中有X这样的字符)
-- 7. 入职时间(取值年月日即可)create table emp
(id int comment '编号',uid varchar(10) comment '员工工号',name varchar(10) comment '员工姓名',gender char(2) comment '性别',age tinyint comment '年龄',id_card char(18) comment '身份证号',entry_date date comment '入职时间'
)comment '员工信息表'
1.4 表修改
-- 1.添加字段
alter table empadd nickname varchar(20)-- 2.表数字段
alter table emp modify nickname varchar (10)-- 3.修改字段类型和字段名
alter table emp change gender sex char (2) comment '性别'-- 4.修改表名
alter table emp rename to employ
1.5 表删除
--1. 删除字段
alter table emp drop nickname-- 2.删除表
drop table if exists tb_user-- 3.清空表TRUNCATE table employdesc empshow tables
2. DML数据操作语言
2.1 插入语句
-- 1.给指定字段添加数据
insert into employ(id, uid, name, sex, age, id_card, entry_date)
values (1, '1', 'Itcast', '男', 38, 123456789, '2000-01-01');
-- 2. 给全部字段添加数据
insert into employ
values (2, '2', '张无忌', '男', 19, 4554656265, '2022-05-30');
-- 3.批量添加数据
insert into employ
values (3, '3', '赵敏', '女', 19, 4354656265, '2022-05-30'),(4, '4', '小昭', '女', 15, 4454656265, '2022-05-30'),(5, '5', '金毛狮王', '男', 50, 4554656265, '2022-05-30'),(6, '6', '段誉', '男', 20, 4654656265, '2022-05-30'),(7, '7', '韦一笑', '男', 19, 4754656265, '2022-05-30');
2.2 修改语句
-- 1.修改id为1的数据,将name修改为itheima
update employ
set name='itheima'
where id = 1;-- 2.修改id为1的数据,将name修改为周芷若,性别修改为女
update employ
set name='周芷若',sex='女'
where id = 1;
-- 3.将所有的员工的入职日期修改为 2008-01-01
update employ set entry_date='2008-01-01';
2.3 删除语句
-- 1.删除性别为女的员工
delete from employ where sex='女';-- 2.删除所有员工
delete from employ;
3.DQL 数据查询语言
3.1 基本查询
-- 1.查询指定字段name, workno, aqe 返回
select name, workno, age
from emp;-- 2.查询所有字段返回
select *
from emp;-- 3.查询所有员工的工作地址,起别名
select workaddress as '工作地址'
from emp;
-- as也可以省略
select workaddress '工作地址'
from emp;-- 4.查询公司员工的上班地址(不要重复)
select DISTINCT workaddress
from emp;
3.2 条件查询
-- 1.查询年龄等于88的员工
select *
from emp
where age = 88;-- 2.查询年龄小于20的员工信息
select *
from emp
where age < 20;-- 3.查询年龄小于等于20的员工信息
select *
from emp
where age <= 20;-- 4.查询没有身份证号的员工信息
select *
from emp
where idcard is null;-- 5.查询有身份证号的员工信息
select *
from emp
where idcard is not null;-- 6.查询年龄不等于88的员工信息
select *
from emp
where not age = 88;-- 7.查询年龄在15岁(包含)到20岁(包含)之间的员工信息
select *
from emp
where age >= 15and age <= 20;select *
from emp
where age >= 15 && age <= 20;select *
from emp
where age between 15 and 20;-- 8.查询性别为女且年龄小于25岁的员工信息
select *
from emp
where age < 25and gender = '女';-- 9.查询年龄等于18或20或40的员工信息
select *
from emp
where age = 18or age = 20or age = 40;select *
from emp
where age in (18, 20, 40);-- 10. 查询姓名为两个字的员工信息
select *
from emp
where name like '__';-- 11. 查询省份证最后一位是X的员工
select *
from emp
where idcard like '%X';select *
from emp
where idcard like '_________________X';
3.3 聚合查询
-- 1.统计该企业员工数量
select count(*) from emp;
-- 用下面这种方式计算,一旦出现了null就会少算
select count(idcard) from emp;-- 2.统计该企业员工的平均年龄
select avg(age) from emp;-- 3.统计该企业员工的最大年龄
select max(age) from emp;-- 4.统计该企业员工的最小年龄
select min(age) from emp;-- 5.统计在西安地区的员工年龄之和
select sum(age) from emp where workaddress='西安';
3.4 分组查询
在分组查询的时候需要使用到group by语句和having语句以及where语句,那么having语句和where语句有什么区别呢?
- 执行时机不同: where是分组之前进行过滤,不满足where条件,不参与分组;而having是分组之后对结果进行过滤。
- 判断条件不同: where不能对聚合函数进行判断,而having可以。
分组查询一般查询分组条件和聚合查询结果
-- 1.根据性别分组,统计男性员工和女性员工的数量
select gender, count(*)
from emp
group by gender;-- 2.根据性别分组,统计男性员工和女性员工的平均年龄
select gender, avg(age)
from emp
group by gender;-- 3.查询年龄小于45的员工,并根据工作地址分组,获取员工数量大于等于3的工作地址
select workaddress, count(*)
from emp
where age < 45
group by workaddress
having count(*) > 3;select workaddress, count(*) as address_count
from emp
where age < 45
group by workaddress
having address_count > 3;
3.5 排序查询
排序查询中排序条件:
- ASC:升序(默认)
- DESC: 降序
如果是多字段排序,首先按照第一个字段排序,然后再按照第二个字段排序。
-- 1.根据年龄对公司的员工进行升序排序
select *
from emp
order by age asc;-- 2.根据入职时间,对员工进行降序排序
select *
from emp
order by entrydate asc;-- 3.根据年龄对公司的员工进行升序排序, 年龄相同, 再按照入职时间进行降序排序
select *
from emp
order by age asc, entrydate desc;
3.6 分页查询
- 语法
SELECT 字段列表 FROM 表名 LIMIT 起始索引,查询记录数;
注意
● 起始索引从0开始,起始索引= (查询页码-1) *每页显示记录数。
● 分页查询是数据库的方言,不同的数据库有不同的实现,MySQL中是LIMIT。
● 如果查询的是第一页数据,起始索引可以省略,直接简写为limit 10。
-- 1.查询第1页员工数据,每页展示10条记录
select * from emp limit 0,10;
-- 第一页可以省略起始索引
select * from emp limit 10;-- 2.查询第2页员工数据,每页展示10条记录-------->因为每页的展示数量是10,则第二页的开始索引是10
select * from emp limit 10,10
3.7 DQL语句练习
-- 1.查询年龄为20,21,22,23岁 的员工信息。
select * from emp where age in(20,21,22,23);-- 2.查询性别为男,并且年龄在20-40岁(含)以内的姓名为三个字的员工。
select * from emp where age between 20 and 40 and name like '___';-- 3.统计员工表中,年龄小于60岁的,男性员工和女性员工的人数。
select gender,count(*) from emp where age<60 group by gender;-- 4.查询所有年龄小于等于35岁员工的姓名和年龄,并对查询结果按年龄升序排序,如果年龄相同按入职时间降序排序。
select name,age from emp where age<35 order by age ASC ,entrydate desc ;-- 5.查询性别为男,且年龄在20-40 岁(含)以内的前5个员工信息,对查询的结果按年龄升序排序,年龄相同按入职时间升序排序。
select * from emp where gender='男' and age between 20 and 40 order by age ASC,entrydate DESC limit 5;
4. DCL 数据控制语言
4.1 用户管理
注意:
- 主机名可以使用%统配
- 这类SQL开发人员操作比较少,主要是DBA(数据库管理员)使用
-- 创建用户itcast ,只能够 在当前主机LocaLhost访问,密码123456;
create user 'itcast'@'localhost' identified by '123456';-- 创建用户heima ,可以在任意主机访问该数据库,密码123456 ;
create user 'itheima'@'%' identified by '123456';-- 修改用户heima 的访问密码为1234
alter user 'itheima'@'%' identified with mysql_native_password by '1234';-- 删除itcast@LocaLhost用户
drop user 'itheima'@'%';
4.2 权限管理
注意:
- 多个权限之间,使用逗号分隔
- 授权时,数据库名和表名可以使用* 进行通配,代表所有。
-- 查询权限
show grants for 'itcast'@'localhost';-- 授予权限
grant all on itcast.* to 'itcast'@'localhost';-- 撤销权限
revoke all on itcast.* from 'itcast'@'localhost';
5.函数
这里只是介绍几种常见的函数,基本函数语法
5.1 字符串函数
-- concat:拼接
select concat('Hello','MySQL');-- lower:转小写
select lower('HellO');-- upper:转大小
select UPPER('Hello');-- lpad:左填充
select lpad('01',5,'-');-- rpad:右填充
select rpad('01',5,'*');-- trim:去除空格
select trim('Hello MySQL');-- substring:截取长度,mysql中的起始索引是从0开始
select substring('hello mysql',1,5);
案例:
update emp set workno=lpad(workno,5,0);
5.2 数值函数
函数语法:
-- ceil:向上取整
select ceil(1.1);-- floor:向下取整
select floor(1.1);-- mod:取模
select mod(3,4);-- rand:随机数
select rand();-- round:四舍五入,保留两位小数
select round(2.34556,2);
举个例子:
-- ceil:向上取整
select ceil(1.1);-- floor:向下取整
select floor(1.1);-- mod:取模
select mod(3,4);-- rand:随机数
select rand();-- round:四舍五入,保留两位小数
select round(2.34556,2);
5.3 日期函数
函数语法:
-- curdate(): 当前日期
select curdate();-- curtime():当前时间
select curtime();-- now():当前的时间和日期
select now();-- 查看年、月、日
select YEAR(now());select MONTH(now());select DAY(now());-- 时间后退
-- 当前时间向前70天
select date_add(now(), INTERVAL 70 DAY);
-- 当前时间向前5个月
select date_add(now(), INTERVAL 5 MONTH);-- datediff: 查询两个时间之间的间隔,第一个时间减去第二个时间
select datediff('2021-10-01', '2023-10-22');
select datediff('2023-10-01', '2021-10-22');
举个例子:
-- 案例: 查询所有员工入职的天数吗,并根据入职天数进行倒叙排序
select name, datediff(now(), entrydate) as '入职天数'
from emp
order by datediff(now(), entrydate) desc;
5.4 流程函数
函数语法:
-- if,第一个参数是需要判断的参数
select if(false, 'ok', 'default');-- ifnull
-- 如果第一个参数是null,就返回第二个,否则就返回第一个
select ifnull('Ok', 'Default');select ifnull('', 'DEFAULT');select ifnull(null, 'default');-- case when then else end
-- 需求查询emp表中的员工姓名和地址(北京/上海---->一线城市,其他----->二线城市)
select name,case workaddress when '北京' then '一线城市' when '上海' then '一线城市' else '二线城市' end as '工作地点'
from emp;
举个例子:
-- --案例:统计班级各个学员的成绩,展示的规则如下:
-- >= 85,展示优秀
-- >= 6日,展示及格
-- 否则,展示不及格
select name,(case when math>=85 then '优秀' when math>=60 then '及格' else '不及格' end) '数学',(case when english>=85 then '优秀' when english>=0 then '及格' else '不及格' end) '英语',(case when chinese>=85 then '优秀' when chinese>=60 then '及格' else '不及格' end) '语文' from score;
6.约束
- 概念:约束是作用于表中字段上的规则,用于限制存储在表中的数据。
- 目的:保证数据库中数据的正确、有效性和完整性。
6.1 约束演示
语法举例,创建表的时候给表添加约束:
-- 创建表格
drop table if exists tb_user;
create table tb_user(id int AUTO_INCREMENT primary key comment 'ID唯一标识',-- 主键约束name varchar(10) not null UNIQUE comment '姓名', -- 非空约束,唯一约束age int check(age>0 && age<120) comment '年龄',-- 检查约束status char(1) default '1' comment '状态',-- 默认约束gender char(1) comment '性别'
);
-- 验证约束
insert into tb_user(name,age,status,gender) values ('Tom1',19,'1','男'),
('Tom2',25,'0','男');insert into tb_user(name,age,status,gender) values ('Tom3',19,'1','男');
insert into tb_user(name,age,status,gender) values (null,19,'1','男');
insert into tb_user(name,age,status,gender) values ('Tom3',19,'1','男');
insert into tb_user(name,age,status,gender) values ('Tom4',80,'1','男');
insert into tb_user(name,age,status,gender) values ('Tom5',-1,'1','男');
insert into tb_user(name,age,status,gender) values ('Tom5',121,'1','男');
insert into tb_user(name,age,gender) values ('Tom5',120,'男');
6.2 外键约束
数据准备:
-- 创建一个部门表
create table dept
(id int primary key comment '部门ID',name varchar(10) comment '部门名称'
);-- 插入数据
insert into dept
values (001, '研发部'),(002, '市场部'),(003, '财务部'),(004, '销售部'),(005, '总经办');
6.2.1 添加外键
创建一个employ表,并且添加外键
-- 创建一个员工表,并给给员工表添加外键
drop table if exists employ;
create table employ
(id int auto_increment comment 'ID' primary key,name varchar(50) not null comment '姓名',age int check (age > 0 && age < 70) comment '年龄',job varchar(20) comment '职位',salary float comment '薪水',entrydate date comment '入职时间',managerid int comment '直属领导ID',dept_id int comment '部门ID' ,constraint fk_employ_dept foreign key (dept_id) references dept(id) -- 可以直接在创建表的时候添加外键
) comment '员工表';
或者外部添加外键:
-- 插入数据
INSERT INTO employ (id, name, age, job, salary, entrydate, managerid, dept_id)
给employ表插入数据
INSERT INTO employ (id, name, age, job, salary, entrydate, managerid, dept_id)
VALUES (1, '金庸', 66, '总裁', 20000, '2000-01-01', null, 5),(2, '张无忌', 20, '项目经理', 12500, '2005-12-05', 1, 1),(3, '杨逍', 33, '开发', 8400, ' 2000-11-03', 2, 1),(4, '韦一笑', 48, '开发', 11000, ' 2002-02-05', 2, 1),(5, '常遇春', 43, '开发', 10500, '2004-09-07', 3, 1),(6, '小昭', 19, '程序员鼓励师', 6600, '2004-10-12', 2, 1);
6.2.2 删除外键
-- 删除外键
alter table employ drop foreign key fk_employ_dept;
6.2.3 外键更新\删除行为
-- CASCADE:级联更新删除
alter table employadd constraint fk_employ_dept foreign key (dept_id) references dept(id) on update cascade on delete cascade;-- SET NULL:设置为空
alter table employadd constraint fk_employ_dept foreign key (dept_id) references dept(id) on update set null on delete set null;
7.多表查询
7.1 多表关系
7.1.1 一对多(多对一)
- 案例: 部门 与 员工的关系
- 关系: 一个部门对应多个员工,一个员工对应一个部门
- 实现: 在多的一方建立外键,指向一的一方的主键
7.1.2 多对多
- 案例: 学生 与 课程的关系
- 关系: 一个学生可以选修多门课程,一门课程也可以供多个学生选择
- 实现: 建立第三张中间表,中间表至少包含两个外键,分别关联两方主键
脚本代码:
-- 创建学生表
create table student
(id int auto_increment primary key comment '主键ID',name varchar(20) comment '姓名',no char(11) comment '学号'
) comment '学生表';-- 创建课程表
create table course
(id int auto_increment primary key comment '主键ID',name varchar(10) comment '课程名称'
) comment '课程表';-- 创建中间表
create table student_course
(id int auto_increment primary key comment '主键ID',student_id int comment '学生ID',course_id int comment '课程ID',constraint fk_course_id foreign key (course_id) references course (id),constraint fk_student_id foreign key (student_id) references student (id)) comment '学生课程中间表';-- 插入数据
insert into student
values (null, '黛绮丝', '2000100101'),(null, '谢逊', '2000100102'),(null, '殷天正', '2000100103'),(null, '韦一笑', '2000100104');insert into course
values (null, 'Java'),(null, 'PHP'),(null, 'MySQL'),(null, 'Hadoop');
7.1.3 一对一
- 案例: 用户 与 用户详情的关系
- 关系: 一对一关系,多用于单表拆分,将一张表的基础字段放在一张表中,其他详情字段放在另
一张表中,以提升操作效率 - 实现: 在任意一方加入外键,关联另外一方的主键,并且设置外键为唯一的(UNIQUE)
脚本代码
drop table if exists tb_user;
create table tb_user
(id int auto_increment primary key comment '主键ID',name varchar(10) comment '姓名',age int comment '年龄',gender char(1) comment '1: 男 , 2: 女',phone char(11) comment '手机号'
) comment '用户基本信息表';create table tb_user_edu
(id int auto_increment primary key comment '主键ID',degree varchar(20) comment '学历',major varchar(50) comment '专业',primaryschool varchar(50) comment '小学',middleschool varchar(50) comment '中学',university varchar(50) comment '大学',userid int unique comment '用户ID',constraint fk_userid foreign key (userid) references tb_user (id)
) comment '用户教育信息表';insert into tb_user(id, name, age, gender, phone)
values (null, '黄渤', 45, '1', '18800001111'),(null, '冰冰', 35, '2', '18800002222'),(null, '码云', 55, '1', '18800008888'),(null, '李彦宏', 50, '1', '18800009999');insert into tb_user_edu(id, degree, major, primaryschool, middleschool,university, userid)
values (null, '本科', '舞蹈', '静安区第一小学', '静安区第一中学', '北京舞蹈学院', 1),(null, '硕士', '表演', '朝阳区第一小学', '朝阳区第一中学', '北京电影学院', 2),(null, '本科', '英语', '杭州市第一小学', '杭州市第一中学', '杭州师范大学', 3),(null, '本科', '应用数学', '阳泉第一小学', '阳泉区第一中学', '清华大学', 4);
7.2 多表查询概述
7.2.1 数据准备
执行下面的脚本,创建对应的表,并且添加数据:
-- 删除其他数据
drop table employ;
drop table if exists dept;
drop table if exists emp;-- 创建dept表,并插入数据
create table dept
(id int auto_increment comment 'ID' primary key,name varchar(50) not null comment '部门名称'
) comment '部门表';INSERT INTO dept (id, name)
VALUES (1, '研发部'),(2, '市场部'),(3, '财务部'),(4,'销售部'),(5, '总经办'),(6, '人事部');-- 创建emp表,并插入数据
create table emp
(id int auto_increment comment 'ID' primary key,name varchar(50) not null comment '姓名',age int comment '年龄',job varchar(20) comment '职位',salary int comment '薪资',entrydate date comment '入职时间',managerid int comment '直属领导ID',dept_id int comment '部门ID'
) comment '员工表';-- 添加外键
alter table empadd constraint fk_emp_dept_id foreign key (dept_id) references dept (id);INSERT INTO emp (id, name, age, job, salary, entrydate, managerid, dept_id)
VALUES (1, '金庸', 66, '总裁', 20000, '2000-01-01', null, 5),(2, '张无忌', 20, '项目经理', 12500, '2005-12-05', 1, 1),(3, '杨逍', 33, '开发', 8400, '2000-11-03', 2, 1),(4, '韦一笑', 48, '开发', 11000, '2002-02-05', 2, 1),(5, '常遇春', 43, '开发', 10500, '2004-09-07', 3, 1),(6, '小昭', 19, '程序员鼓励师', 6600, '2004-10-12', 2, 1),(7, '灭绝', 60, '财务总监', 8500, '2002-09-12', 1, 3),(8, '周芷若', 19, '会计', 48000, '2006-06-02', 7, 3),(9, '丁敏君', 23, '出纳', 5250, '2009-05-13', 7, 3),(10, '赵敏', 20, '市场部总监', 12500, '2004-10-12', 1, 2),(11, '鹿杖客', 56, '职员', 3750, '2006-10-03', 10, 2),(12, '鹤笔翁', 19, '职员', 3750, '2007-05-09', 10, 2),(13, '方东白', 19, '职员', 5500, '2009-02-12', 10, 2),(14, '张三丰', 88, '销售总监', 14000, '2004-10-12', 1, 4),(15, '俞莲舟', 38, '销售', 4600, '2004-10-12', 14, 4),(16, '宋远桥', 40, '销售', 4600, '2004-10-12', 14, 4),(17, '陈友谅', 42, null, 2000, '2011-10-12', 1, null);
7.2.2 连接查询
-- 内连接演示
-- 1.查询每一个员工的姓名,及关联的部门的名称(隐式内连接实现):查询的是两张表交集的部分
-- 表结构:emp,dept
-- 连接条件:emp.dept_id=dept.id
select emp.name, dept.name
from emp,dept
where emp.dept_id = dept.id;-- 使用别名,使用了别名就不能使用表名操作
select e.name, d.name
from emp e, dept d
where e.dept_id = d.id;-- 2.查询每一个员工的姓名,及关联的部门的名称(显式内连接实现) --- INNER JOIN...ON..
-- 表结构:emp,dept
-- 连接条件:emp.dept_id=dept.id
select e.name, d.name
from emp e inner join dept d on e.dept_id = d.id;select e.name, d.name
from emp e join dept d on e.dept_id = d.id;-- 外连接
-- 左外连接:完全包含左表和与右表交集的数据
-- 1. 查询emp表的所有数据, 和对应的部「1信息(左外连接)
-- 表结构:emp,dept
-- 连接条件:emp.dept_id=dept.id
select e.*, d.name
from emp e left join dept d on d.id = e.dept_id;-- 右外连接:完全包含右表和与左表交集的数据
-- 2.查询dept表的所有数据, 和对应的员工信息(右外连接)
select d.*, e.*
from emp e right join dept d on d.id = e.dept_id;
7.2.3 自查询
自查询是同一个表进行关联查询,所以每一个表都必须有自己的别名
-- 1.查询员工及其所属领导的名字
-- 表必须取别名
select a.name, b.name as '上级领导'
from emp a join emp b on a.managerid = b.id;select a.name, b.name as '上级领导'
from emp a, emp b where a.managerid = b.id;-- 2.查询所有员工emp及其领导的名字emp,如果员 工没有领导,也需 要查询出来
select a.name, b.name as '上级领导'
from emp a left join emp b on a.managerid = b.id;
7.2.4 联合查询
联合查询就是将两张表合并到一起,举个例子:
-- 查询员工表中薪水小于5000和年龄大于50的员工信息
select * from emp where salary<5000
union
select * from emp where age>50
7.2.5 子查询
- 标量子查询
-- 1.查询“销售部门”的所有员工的信息
-- a.查询销售部的id
select id from dept where name='销售部';-- b.根据销售部的id 查询员工的信息
select * from emp where dept_id=(select dept.id from dept where name='销售部');-- 2.查询在方东白入职之间的员工信息
select * from emp where entrydate>(select entrydate from emp where name='方东白');
- 列子查询
- 1.查询‘销售部’和‘市场部’的所有员工的信息
select * from emp where dept_id in (select dept.id from dept where name in('市场部' , '销售部'));-- 2.查询比财务部所有人工资都高的人的信息
select * from emp where salary>all (select salary from emp where dept_id =(select dept.id from dept where name='财务部'));-- 3.查询比研发部中任意一人工资高的员工的信息
select * from emp where salary>any (select salary from emp where dept_id =(select dept.id from dept where name='研发部'));
- 行子查询
- 子查询返回的结果是一行(可以是多列),这种子查询称为行子查询。
- 常用的操作符: = 、<>、IN、NOT IN
-- 1.查询与"张无忌”的薪资及直属领导相同的员工信息;
-- a.查询"张无忌”的薪资及直属领导
select salary,managerid from emp where name='张无忌';-- b.查询与"张无忌”的薪资及直属领导相同的员工信息;
select * from emp where (salary,managerid)=(12500,1);
select * from emp where (salary,managerid)=(select salary,managerid from emp where name='张无忌');
- 表子查询
- 子查询返回的结果是多行多列,这种子查询称为表子查询。
- 常用的操作符: IN
-- 1.查询与"鹿杖客”,“宋远桥”的职位和薪资相同的员工信息
-- a.查询"鹿杖客”,"宋远桥”的职位和薪资
select job,salary from emp where name='鹿杖客'or name='宋远桥';-- b.查询与"鹿杖客”,"宋远桥”的职位和薪资相同的员工信息
select * from emp where (job,salary) in (select job,salary from emp where name='鹿杖客'or name='宋远桥');-- 2.查询入职日期是"2006-01-01” 之后的员工信息,及其部门信息
-- a.入职日期是"2006-01-01” 之后的员工信息
select * from emp where entrydate > ' 2006-01-01';-- b.查询这部分员工,对应的部门信息;
select e.*,d.* from(select * from emp where entrydate>'2006-01-01')e left join dept d on e.dept_id=d.id;
8.事务
8.1 事务简介
事务
是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败。
就比如: 张三给李四转账1000块钱,张三银行账户的钱减少1000,而李四银行账户的钱要增加
1000。 这一组操作就必须在一个事务的范围内,要么都成功,要么都失败。
8.2 事务操作
8.2.1 数据准备
首先创建一个account表,模拟银行转账操作:
drop table if exists account;
create table account(
id int primary key AUTO_INCREMENT comment 'ID',
name varchar(10) comment '姓名',
money double(10,2) comment '余额'
) comment '账户表';
当前account数据库中的结果:
8.2.2 模拟转账操作(引例)
-- 1.查询张三账户余额
select money from account where name='张三';-- 2.将张三账户余额-1000
update account set money=money-1000 where name='张三';-- 3.将李四账户余额+1000
update account set money=money+1000 where name='李四';
转账成功;如果此时第三步的时候出现了异常,会出现什么情况呢?
-- 1.查询张三账户余额
select money from account where name='张三';-- 2.将张三账户余额-1000
update account set money=money-1000 where name='张三';程序抛出异常...
-- 3.将李四账户余额+1000
update account set money=money+1000 where name='李四';
此时就会出现张三转账过去了,但是李四没有收到钱的情况,为了解决这个问题就需要使用到事务。mysql中的每条语句都是具有事务的,并且是自定提交了,因此我们就需要把上面几条sql语句绑定成一个事务中。
8.2.3 方式一:手动提交事务
-- 查询当前的事务状态
-- 方式1:
select @@autocommit;-- 等于1,说明是自动提交事务,等于0,说明是手动提交事务set @@autocommit=0;-- 设置为手动提交事务-- 1.查询张三账户余额
select money from account where name='张三';-- 2.将张三账户余额-1000
update account set money=money-1000 where name='张三';-- 3.将李四账户余额+1000
update account set money=money+1000 where name='李四';
如果上面的执行过程没有任何问题,就执行提交操作就可以了,这样就是把修改的数据保存到数据库中。
commit;
如果上面的执行做成中有问题,就执行回滚操作,这样就会把之间的操作还原到原来的状态;
rollback;
8.2.4 方式二:自动提交事务
先设置提交方式为自动提交:
set @@autocommit=1;-- 设置为自动提交事务
-- 开启事务
start transaction ;-- 1.查询张三账户余额
select money from account where name='张三';-- 2.将张三账户余额-1000
update account set money=money+1000 where name='张三';-- 3.将李四账户余额+1000
update account set money=money-1000 where name='李四';
如果上面的执行没有问题,则就commit
commit;
如果上面的执行有问题,则就rollback
rollback;
8.3 事务的特性
- 原子性(Atomicity) :事务是不可分割的最小操作单元,要么全部成功,要么全部失败。
- 一致性(Consistency) :事务完成时,必须使所有的数据都保持一致状态。
- 隔离性(Isolation):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行。
- 持久性(Durability) :事务一旦提交或回滚,它对数据库中的数据的改变就是永久的。
事务的四大特性简称为ACID
8.4 并发事务问题
问题 | 描述 |
---|---|
脏读 | -个事务读到另外一一个事务还没有提交的数据。 |
不可重复读 | -个事务先后读取同一条记录,但两次读取的数据不同,称之为不可重复读。 |
幻读 | 一个事务按照条件查询数据时,没有对应的数据行,但是在插入数据时,又发现这行数据已经存在,好像出现了幻影”。 |
8.4.1脏读
假如A事务中有查询和更新两个操作,在A事务还没执行完之间,事务B就开始对数据库进行查询,则查询到的数据就是未修改的数据,等它查询完之后,数据库也被修改了。
8.4.2 不可重复读
首先A事务对数据库进行查询操作,在A事务还没有执行完之前,事务B对数据库进行了修改,此时事务A又对数据库进行一次查询操作,此时的查询结果与第一次查询的结果就不一致了,这种情况就被称为不可重复读。
8.4.3 幻读
假设解决了不可重复读的问题。
首先事务A先对数据库查询id为1的数据,但是没有查询到,所以它的下一步是将这个没有查询到的数据插入进去,在这步操作前,假设事务B对数据库已经完成了插入操作,此时事务A再向数据库中插入id为1的数据,此时却插入不了,因为id为主键,数据库中已经存在了id为1的数据,但是再查询这个数据却也查不到,因为解决了不可重复读的问题,这个过程就好像发生了幻觉一样,因此被称为幻读。
8.5 事务隔离级别
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
Read Uncommitted(已读未提交) | √ | √ | √ |
Read Commit(已读提交) | × | √ | √ |
Repeatable Read(可重复读) | × | √ | √ |
Serializable(串行化) | × | × | × |
mysql的默认事务隔离级别是Repeatable Read
注意:事务的隔离级别越高,数据约安全,但是性能越低;
8.5.1 事务隔离级别操作语句
设置事务隔离级别的语法
set [session | global] transaction isolation level {read uncommitted | read committed | repeatable read | serializable}
-- 查看事务的隔离级别,默认的隔离级别就是REPEATABLE-READ
select @@transaction_isolation;-- 设置隔离级别
set session transaction isolation level read uncommitted ;
set session transaction isolation level REPEATABLE READ;
8.5.2 验证每种隔离级别是否解决了对应的问题
模拟并发事务验证上面的结果:
- 验证Read Uncommitted是否会出现脏读
- 验证read committed是否解决了脏读问题
- 验证read committed没有解决不可重复读问题
- 验证repeatable read可以解决事务的不可重复读问题
- 验证repeatable read不能解决幻读问题
- 验证serializable是否能够解决幻读问题