带你了解什么是MySQL数据库(四)
目录
- 单表查询语句
- 联表查询
- 交叉连接
- 内连接
- 左连接
- 右连接
- 子查询
单表查询语句
多个语句的使用方式,以及优先级
where 条件
group by 分组字段
having 条件(通常跟在group by 后面判断)
order by 排序字段
limit 限制条数
distinct 去重(去掉相同字段重复数据)
下面再来逐个使用介绍
我们先来创建如下实验表:
create table employee(
id int not null unique auto_increment,
name varchar(20) not null,
sex enum('male','female') not null default 'male', # 填写的值必须在enum内,如果不填则默认为male
age int(3) unsigned not null default 28,
hire_date date not null,
post varchar(50),
post_comment varchar(100),
salary double(15,2),
office int,
depart_id int
);
插入数据
#三个部门:教学,销售,运营
insert into employee(name,sex,age,hire_date,post,salary,office,depart_id) values
('jack','male',18,'20170301','外交大使',7300.33,401,1), #以下是教学部
('tom','male',19,'20150302','teacher',1000000.31,401,1),
('jams','male',23,'20130305','teacher',8300,401,1),
('rouse','male',21,'20140701','teacher',3500,401,1),
('liwenzhou','male',28,'20121101','teacher',2100,401,1),
('jingliyang','female',18,'20110211','teacher',9000,401,1),
('jinxin','male',18,'19000301','teacher',30000,401,1),
('张三','male',48,'20101111','teacher',10000,401,1),('歪歪','female',48,'20150311','sale',3000.13,402,2),#以下是销售部门
('丫丫','female',38,'20101101','sale',2000.35,402,2),
('丁丁','female',18,'20110312','sale',1000.37,402,2),
('星星','female',18,'20160513','sale',3000.29,402,2),
('格格','female',28,'20170127','sale',4000.33,402,2),('张野','male',28,'20160311','operation',10000.13,403,3), #以下是运营部门
('程咬金','male',18,'19970312','operation',20000,403,3),
('程咬银','female',18,'20130311','operation',19000,403,3),
('程咬铜','male',18,'20150411','operation',18000,403,3),
('程咬铁','female',18,'20140512','operation',17000,403,3)
;
我们现在查询部门
有没有发现很多重复的内容,我们只需要显示出有哪些, 单个效果加上去重。
select distinct post from employee;
它并不是对字段去重,而是对显示出来的记录去重
算出每个用户用户的年薪
select name,salary*12 from employee;
这样就算出了每个用户的年薪,但是标题却变成了这样。我们可以自定义标题
select name,salary*12 as 年薪 from employee; # 第一种方式:自定义别名
select name 名字,salary*12 年薪, # 第二种方式:省略as定义别名
但是推荐使用as,比较明确一些。
数据内容拼接,在已有的表数据基础上,显示出来时拼接一些其他内容。
select concat('名字:',name), concat('薪资:',salary) from employee;
无语…虽然显示出效果,但是它这个虚拟表的标题又是我们执行操作时的内容,使用as定义别名就可以解决。
select concat('名字:',name) as name , concat('薪资:',salary) as salary from employee;
将多个字段的内容拼接到一起
select concat_ws(":",name,post,salary) as Information from employee;
将多个字段的内容,通过concat_ws函数显示在一起,并使用指定分隔符。
多条件判断,筛选记录
SQL本质上也是一种编程语言,越向后使用,越明确,变量,函数,循环,判断等等都有
我们可以在查询时,进行多分支判断。使用case语句,根据判断不同内容,显示不同结果
SELECT(CASE # 开始WHEN NAME = 'jack' THEN # name字段数据是否为jack,是的话则执行其下面的concatconcat(name,'_SuperNb')WHEN NAME = 'tom' THEN # 如果name字段数据不是jack的情况下,则判断是否为tomconcat(name,'_VeryNb')ELSE # 如果name字段数据不是jack和tom的情况下,执行这行判断concat(NAME, 'Nb')END # 结束) as new_nameFROMemployee;
我们还可以在update更新值时,修改多个符合我们要求的值
实例:将性别替换
| id | name | sex | salary |
|----|------|-----|--------|
| 1 | A | m | 2500 |
| 2 | B | f | 1500 |
| 3 | C | m | 5500 |
| 4 | D | f | 500 |# 将以上表内容替换成以下| id | name | sex | salary |
|----|------|-----|--------|
| 1 | A | f | 2500 |
| 2 | B | m | 1500 |
| 3 | C | f | 5500 |
| 4 | D | m | 500 |# 可以发现只有sex里面的数据与原数据进行相反的替换update salary set sex = CASE when sex='m' then 'f'when sex = 'f' then 'm'end# 一条update语句就可以解决
where条件可以使用的语句
- 比较运算符:> < >= <= !=
- between 10 and 20 值在10到20之间
- in(10,20,30) 值是10或20或30
- 逻辑运算符:在多个条件直接可以使用逻辑运算符 and or not
实例:
# 第一种写法:
select id,name,salary from employee where id >= 10 and id <= 13;# 第二种写法:
select id,name,salary from employee where id between 10 and 13;# 如果是这种判断,推荐第二种写法
or判断 和 in集合判断
# 第一种写法:
select id,name,salary from employee where id = 10 or id = 11 or id = 13;# 第二种写法:推荐
select id,name,salary from employee where id in (10,11,13);
判断某个字段是否为Null,使用is关键字,不需要=
我们先设置两条记录的字段不为空,方便实验
update employee set post_comment = "" where id < 3;
# 注意:我们给post_comment设置了空字符串,但是它不代表null
实例:
# 判断post_comment字段为空的记录
select * from employee where post_comment is null;# 判断post_comment字段不为空的记录
select * from employee where post_comment is not null;
字段分组
定义某一字段进行分组后,会出去该字段下面的重复数据(具有去重效果),然后每个归为一组
实例:
select post from employee group by post;
聚合函数
目前先介绍几个常用的
最大值:max(字段名)
最小值:max(字段名)
平均值:avg(字段名)
求和:sum(字段名)
数量:count(字段名)
我们可以操作分组后的字段对应的数据。
# 获取每个组薪资的最大值
select post,max(salary) as '最大值' from employee group by post;# 获取每个组的记录的数量
select post,count(id) as '数量' from employee group by post;
案例1:取出每个部门男员工的平均薪资
select post,avg(salary) as '男员工平均薪资' from employee where sex = 'male' group by post;# 先把男员工判断出来,再将部门分组
案例2:取出每个部门年龄 > 20岁的员工平均薪资
select post,avg(salary) from employee where age > 20 group by post;# 先将年龄大于20的判断出来,再将部门分组
分完组以后,取的是分组字段和聚合的结果,虽然使用*也可以查询到每个组第一条记录的结果,但是我们分组的目的一定是取某个组的聚合结果。
分组一定是容易区分的字段进行分组,那么我们可以对id字段分组吗?结果是可以的,但是分组以后没有什么区别,因为每个id都不一样,如果是性别就可以作为分组目标了,因为它的数据是很多条记录都在重复使用的。
having过滤
HAVING与WHERE不一样的地方在于
!!!执行优先级从高到低:where > group by > having
1、Where 发生在分组group by之前,因而Where中可以有任意字段,但是绝对不能使用聚合函数。
2、Having发生在分组group by之后,因而Having中可以使用分组的字段,无法直接取到其他字段,可以使用聚合函数
案例:取出男员工的平均薪资大于10000的部门
我们首先考虑的是,在分组之前要过滤出什么?男员工,分组之后过滤的就是平均薪资大于10000的部门。
select post,avg(salary) as '平均薪资' from employee where sex = 'male' group by post having avg(salary) > 10000;
注意:where后面不可以使用聚合函数 ,为什么不可以?
聚集函数也叫列函数,它们都是基于整列数据进行计算的,而where子句则是对数据行进行过滤的,在筛选过程中依赖“基于已经筛选完毕的数据得出的计算结果”是一种悖论,这是行不通的。更简单地说,因为聚集函数要对全列数据时行计算,因而使用它的前提是:结果集已经确定!再简单一点说就是:数据还没有完整读完,所以不能使用聚合函数
而为什么group by后面可以使用聚合函数呢,因为数据已经过读出来了,使用group by只是进行了分组而已,所以可以使用聚合函数。
order by分段排序
在分组之后使用,可以指定某个字段进行排序,两种排序方式:升序、降序
- asc升序
- desc降序
案例:按薪资字段降序排序
select name,salary from employee order by salary desc;
order by还可以进行多次排序,第二次的排序基于第一次的排序后的结果进行排序
实例:
select id,name,age from employee order by age;
我们并未指定排序方式,所以默认是desc升序的,但此时出现了很多相同的年龄,我们在这次的基础上再次排序
select id,name,age from employee order by age asc,id desc;
这里就是演示了一下,排序可以多次
案例:取出男员工平均薪资大于10000的部门,并且按照平均薪资降序排序
select post as '部门',avg(salary) as '平均薪资' from employee where sex='male' group by post having avg(salary) > 10000 order by avg(salary) desc;
上序操作综合使用我们所练习的所有内容;
limit显示条数
select id,name,age from employee limit 3;
案例:取出薪资最高的那条记录
select name,salary from employee order by salary desc limit 1;
当然,也有可能存在薪资相同的情况,可以思考下如何解决?但也不必过于纠结。
我们也可以指定显示的条数,从n开始向n条数显示的条数
select * from employee limit 1,3; # 从第一条记录向后显示3条记录
select * from employee limit 5,3; # 从第5条记录向后显示5条记录
regexp正则匹配数据
在上篇了解到了模糊查询,如果需要更详细搜索内容,需要使用到正则匹配
select name,age from employee where name regexp '^ja.k';
正则的匹配规则都是差不多的,可以看一下笔者整理的正则相关内容:正则表达式
不需要看其它代码,只看字符串里面的匹配规则即可
联表查询
将物理表(真实存于硬盘上面的表)之间通过SQL语句关联起来,形成一个虚拟表,再筛选出我们想要的记录
同时查询多张有关联性的表,拿到我们想要的数据,呈现在虚拟表里面。也可以多次查询相同的表
先创建实验所需要的两张表
部门表
create table dep(id int primary key auto_increment,name varchar(16) not null unique key,task varchar(16) not null);insert dep(name,task) values('IT','技术'),('HR','招聘'),('sale','销售');
员工表
create table emp(id int primary key auto_increment,name varchar(10) not null,dep_id int,foreign key(dep_id) references dep(id) on update cascade # 级联更新on delete cascade); # 级联删除insert emp(name,dep_id) values('jack',1),('tom',2),('jams',1),('rouse',3),('curry',2);# ('go',4) 报错,在关联外键的id字段中找不到
交叉连接
交叉连接所返回结果即为笛卡尔积,即:对于两个不同的集合A和B,对于A中的每一个元素,都有对于在B中的所有元素做连接运算。因此对于两个元组分别为m,n的表,笛卡尔积后得到的元组个数为m x n。
由于交叉连接与笛卡尔积结果相同,但存在两种不同写法:
写法1:交叉连接
select * from dep d cross join emp e; # 表后面的简写字母,代表别名
写法2:笛卡尔积
select * from dep d,emp e;
这种写法,相同与左边表(dep)的每一条记录对应右边表(emp)全部记录(相同与两个for循环)
要想从中取出我们想要的数据,就要进行过滤筛选,只保留有对应关系的记录。
案例:查询IT部门的所有人数
select e.name,d.name as '部门',d.task from dep d,emp e where d.name = 'IT' and d.id = e.dep_id;
分析:先筛选出所有IT部门,然后在员工表里面dep_id字段查找和IT部门ID相匹配的记录。
而这种方式适用于两个有关联的表查询,而不适用多张表联查,所以需要了解到一种新的方式,支持多张表查询。上面内容了解即可
内连接
只保留有对应关系的记录
先创建几张表,借助上一篇多对多关联里面的表,实现多表查询的效果
book表
create table book(id int primary key auto_increment,name varchar(30));
author表
create table author(id int primary key auto_increment,name varchar(30));
中间表:负责将两张表进行关联
create table authorRbook(id int primary key auto_increment,author_id int,book_id int,foreign key(book_id) references book(id)on update cascadeon delete cascade,foreign key(author_id) references author(id)on update cascadeon delete cascade);
多名作者关联一本书,或者一名作者关联多本书,书也要体现出谁关联了它
book表插入数据:
insert book(name) values('斗破苍穹'),('斗罗大陆'),('武动乾坤');
author表插入数据:
insert author(name) values('jack'),('tom'),('jams'),('rouse'),('curry'),('john');
关联表插入数据:
insert authorRbook(author_id,book_id) values(1,1),(1,2),(1,3),(2,1),(2,3),(3,2),(4,1),(5,1),(5,3),(6,2);
目前的对应关系就是:
jack:斗破苍穹、斗罗大陆、武动乾坤
tom:斗破苍穹、武动乾坤
jams:斗罗大陆
rouse:斗破苍穹
curry:斗破苍穹、武动乾坤
jhon:斗罗大陆
那么我们再通过内连接的方式,将几张表连接在一起。
select * from authorRbook ab inner join author a inner join book b;
这时我们会发现,很多记录重复了很多次,这是因为每张表的记录结合到了一起,所以有些记录少的表,它会重复打印很多次。
案例:找出一本书参入编写的所有作者
select a.name,b.name from authorRbook ab inner join author a inner join book b on b.name = '武动乾坤' and ab.book_id = b.id where ab.author_id = a.id;
上面操作提到了on
on作为连接条件,在联表查询时,on是一个条件,首先会过滤掉不符合条件的记录,再进行统计。
上序操作分析:
先将每张表联合起来,再给每张表取一个别名,在查询之前先在book表中过滤出name为"武动乾坤"的记录,再拿这个记录的ID与中间表的book_id对应,拿到匹配的记录,再将匹配的记录里面author_id字段数据与author表的ID对应,就可以拿到作者的信息了。
最终效果:
只要联表查询,都可以使用内连接:inner join
,当然还有另外两种联表方式,分别是左连接
与右连接
分别代表的是:
- 左连接:当条件不满足时,以左边的表为主
- 右连接:当条件不满足时,以右边的表为主
where与on的区别
where是对最终的临时表进行筛选,而on是作为匹配条件筛选
on后面进行多条件判断需要连续使用and,但必须每个and条件都成立才生效,而在on后面使用where的话,则表示,如果on匹配的条件不成立,where也可以继续匹配
演示:左连接与右连接的区别
左连接
在内连接的基础上,将左边表的记录保留下来,不管是否有对应关系或判断是否成功
先创建两张表
publisher表
CREATE TABLE `publisher` (`id` int primary key AUTO_INCREMENT COMMENT '出版社编号',`name` varchar(20) NOT NULL COMMENT '出版社名称',`linkman` varchar(20) NOT NULL COMMENT '负责人',`tel` varchar(20) NOT NULL COMMENT '出版社联系电话',`address` varchar(50) NOT NULL COMMENT '出版社详细地址'
) ;insert publisher values(null,'深圳大学出版社','王五','1101001','深圳'),
(null,'北京大学出版社','李四','1203001','北京'),
(null,'清华大学出版社','张小明','1302001','北京'),
(null,'厦门大学出版社','李小','12030123','厦门');
book表
CREATE TABLE `book`(
`id` int primary key AUTO_INCREMENT COMMENT '图书编号',
`name` varchar(20) NOT NULL COMMENT '图书名称',
`p_id` int COMMENT '出版社编号',
`author` varchar(20) NOT NULL COMMENT '图书作者',
`price` float NOT NULL COMMENT '图书定价',
foreign key(p_id) references publisher(id)on update cascade
on delete cascade);insert book values(null,'斗破苍穹',2,'天蚕土豆',85),
(null,'斗罗大陆',2,'唐家三少',78.5),
(null,'帝霸',1,'萧生',72),
(null,'元尊',3,'天蚕土豆',68),
(null,'龙族',null,'龙煞',99.9);
通过左连接:left join联表查询
select b.name,b.author,p.name,p.address,b.p_id from book b left join publisher p on b.p_id = p.id;
可以看到,其中龙族这本书不属于任何出版色,且没有判断成功,但还是显示出来了,那是因为使用的是左连接,book表处于左边,所以不管条件是否判断成功,都会显示出来记录。
右连接
在内连接的基础上,将右边表的记录保留下来,不管是否有对应关系或判断是否成功
select b.name,b.author,p.name,p.address,b.p_id from book b right join publisher p on b.p_id = p.id;
可以看到,显然book表内的p_id字段没有与厦门大学出版社
对应的id,但是还是显示出来了,因为publisher
表在right join
右边。
子查询
不同于内连接查询,子查询是将某一张表的查询结果,给予外部查询语句作为查询条件,而内连接则是同时打开所有的表进行查询。
- 子查询是将一个查询语句嵌套在另一个查询语句中
- 内层查询语句的查询结果,可以为外层查询语句提供查询条件
- 子查询中可以包含:IN、NOT IN、ANY、ALL、EXISTS 和 NOT EXISTS等关键字
- 还可以包含比较运算符:= 、 !=、> 、<等
在实验开始前,我们先创建如下实验表:
dep部门表
create table dep(id int primary key auto_increment,name varchar(20)
);insert dep(name) values('RD'),('FE'),('UI'),('QA');
emp员工表
create table emp(id int primary key auto_increment,name varchar(20),age int,dep_id int
);insert emp(name,age,dep_id) values('jack',18,2),('tom',20,1),('jams',23,1),('rouse',19,3);
带in关键字的子查询
实例:查询某个部门下的员工
select id,name,dep_id from emp where dep_id in (select id from dep where
name='RD');
分析:首先将括号内的查询语句执行完毕,拿到执行后的结果(也就是RD部门的id编号),然后将这些结果通过in与dep_id匹配,是否有符合的。
上面就是相当于 dep_id in (1)
带any关键字的子查询
any和in有相同之处就是在括号内匹配某一内容,不同之处在于,any需要加上比较运算符,且any(结果集),也就是里面必须是一个能产生结果的语句,而不能我们自己填写数字。
每个部门员工的平均年龄
实例:筛选出大于 平均年龄最低部门的 员工信息
select id,name,age from emp where age > any(select avg(age) from emp group by dep_id);
平均年龄最低的部门是jack所在部门(18.0),每次都会拿到每个部门的平均年龄与员工年龄比较,只要员工年龄大于其中某个则成立。
带all关键字的子查询
使用all关键字后,我们的判断必须满足括号内的每个结果集
我们先查询员工平均年龄
select avg(age) as '平均年龄' from emp;
实例:筛选出大于所有部门平均年龄的员工信息
select * from emp where age > all(select avg(age) from emp group by dep_i
d);
带比较运算符的子查询
实例:筛选出小于平均年龄的员工
select * from emp where age < (select avg(age) from emp);
带exists的子查询
exists只会产生两种结果:True、False
select * from emp where exists(select * from emp);
能够显示出现是因为exists产生了True结果(括号有有产生内容返回)
演示exists产生False结果
select * from emp where exists(select * from emp where id > 100);
这里是因为括号内没有返回结果,所以exists产生了False
补充内容:not in无法识别null
实例:找到没有员工的部门
# 我们在员工表里面添加一条记录
insert emp values(null,'iii',30,null);select * from emp;
select * from dep;
目前可以很简单看出哪个部门没有员工,这是因为数据量小,如果数据量庞大,还能直接看出来吗?所以我们需要了解查询方法。
首先需要分析:emp表里面的dep_id是否全部对应dep表里面的id,如果dep表里面的某个id值一直没有被对应,说明这个部门没有员工。
实例:
select * from dep where id not in(select dep_id from emp);
上面代码表示:如果dep表里面的某个部门id不在emp表的dep_id内,说明该部门没有员工
但是为什么没有显示内容出来?这是因为emp表里面有一个员工的dep_Id为null,而not in 无法识别,所以给我们造成了一种错觉,以为每个部门都有员工。
修改SQL语句:
select * from dep where id not in (select dep_id from emp where dep_id is not null);
先将emp表内dep_id字段的null数据排除掉,再拿到有部门员工的部门id
SQL语句的运行顺序:
- from
- join
- on
- where
- group by
- 聚合函数
- having
- select
- distinct
- order by
- limit
技术小白记录学习过程,有错误或不解的地方请指出,如果这篇文章对你有所帮助请
点赞 收藏+关注
子夜期待您的关注,谢谢支持!
带你了解什么是MySQL数据库(四)相关推荐
- 使用MySQL自带工具mysqlhotcopy快速备份mysql数据库
使用MySQL自带工具mysqlhotcopy快速备份mysql数据库 发表于82 天前 ⁄ 网站备份 ⁄ 暂无评论 mysqlhotcopy是一个Perl脚本,最初由Tim Bunce编写并提供.它 ...
- django 不用自带的mysql_21_django配置使用mysql数据库的两种方式
配置django项目使用mysql数据库的两种方式 1. 直接在settings.py 文件中添加数据库配置信息 # 配置数据库的第一种方式 DATABASES = { 'default': { 'E ...
- 带你了解什么是MySQL数据库(一)
目录 数据库的概念 数据库的分类 关系型数据库 非关系型数据库 SQL语句(了解阶段) mysql配置文件 破解MySQL账号密码 数据库的概念 数据库:即存放数据的一个仓库 使用数据库的优势:可供远 ...
- MYSQL数据库四种索引类型的简单使用
MYSQL数据库索引类型包括普通索引,唯一索引,主键索引与组合索引,这里对这些索引的做一些简单描述: (1)普通索引 这是最基本的MySQL数据库索引,它没有任何限制.它有以下几种创建方式: 创建索引 ...
- MySQL数据库四:MySQL数据库集群
一.主从模式 MySQL主从模式是指数据可以从一个MySQL数据库服务器主节点复制到一个或多个从节点. mysql主从复制用途: 实时灾备,用于故障切换(高可用) 读写分离,提供查询服务(读扩展) 数 ...
- 带你了解什么是MySQL数据库(六)索引原理,如何建立与使用索引
文章目录 前言 索引原理 介绍 查找二叉树.平衡二叉树.B树.B+树 聚集索引与非聚集索引 索引管理 测试索引 正确使用索引 联合索引 索引下推技术 索引优化神器 常见慢查询优化 前言 学习过某一门编 ...
- 瞬间带你了解如何优化 Mysql 数据库,老板再也不担心客户投诉了
Mysql优化(通用版) 一.优化方向 二.优化方法 1.监控分析 2.改变 SQL 执行计划 三.软优化 1.查询语句优化 2.优化子查询 3.使用索引 4.分解表 5.增加中间表 6.增加冗余字段 ...
- 带你了解什么是MySQL数据库(八)数据库锁机制
目录 数据库的锁机制 锁的分类 MySQL中的行级锁,表级锁,页级锁(粒度) 行级锁之共享锁与排他锁(级别) innodb存储引擎的锁机制 行级锁与表级锁区分 三种行锁算法 死锁问题 什么时候使用表锁 ...
- MYSQL数据库四种储存引擎
四种mysql存储引擎 前言 数据库存储引擎是数据库底层软件组织,数据库管理系统(DBMS)使用数据引擎进行创建.查询.更新和删除数据.不同的存储引擎提供不同的存储机制.索引技巧.锁定水平等功能,使用 ...
最新文章
- linux vim配置注释,vim自动添加注释
- 今晚直播 | 来自《简明的 TensorFlow 2》作者,Google开发者专家的分享
- IntelliJ IDEA 、 Android Stadio 不显示Version Contro窗口
- c++调用python返回字典
- 谷歌为雇人监听智能助手录音辩护 承诺对用户数据泄露进行调查
- 启明云端分享|LVGL官方认证的开发板,到底有多牛
- session丢失php,PHP Session丢失无效问题总结
- 解决qt程序运行时的cannot create Qt for Embedded Linux data directory: /tmp/qtembedded-0
- Microsoft Sharepoint server 2.0 (wss) 服务器升级为额外的主域控制器
- rbf神经网络 c语言,RBF神经网络极简介绍及其算法R语言实现
- RH850入门教程序言
- 通过对TCPWindowSize的调整对网络流量的性能优化
- 华为云服务器如何使用
- 2019年CSDN排名前10名大神
- WiFi网络测速专业版
- matlabapp窗口图像_matlab的App designer使用
- 帆软中的日期函数,当月第一天,当年第一天,当月最后一天等
- android 绘画笔迹回放_Android画板 半透明画笔 笔迹叠加效果
- IDEA如何建立 包中包
- (转)flex布局换行后间隙问题