MySQL-复杂查询及条件-起别名-多表查询-04

Posted suwanbin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL-复杂查询及条件-起别名-多表查询-04相关的知识,希望对你有一定的参考价值。

基本查询语句及方法

测试数据创建

如果在windows系统中,插入中文字符,select的结果为空白,可以将所有字符编码统一设置成gbk(或者参照我安装配置MySQL的博客,将所有字符编码设置为 utf8)

创建数据库与表

create database db1;
use db1;

create table emp(
  id int not null unique auto_increment,
  # 表内没有字段是primary key,innodb的机制,一个表没有primaryk key时会自动将 not null + unique的键自动升级为 primary key 主键
  name varchar(20) not null,
  sex enum('male','female') not null default 'male',  # 大部分是男的
  age int(3) unsigned not null default 28,  # 这个3 只限制了显示宽度,并不影响存储
  hire_date date not null,
  post varchar(50),
  post_comment varchar(100),
  salary double(15,2),
  office int,  # 一个部门一间办公室,一个门牌号
  depart_id int  # 暂不建立外键关系
);

插入表记录数据

# 三个部门:教学,销售,运营
# 以下是教学部
insert into emp(name,sex,age,hire_date,post,salary,office,depart_id) values
('jason','male',18,'20170301','张江第一帅形象代言',7300.33,401,1),
('egon','male',78,'20150302','teacher',1000000.31,401,1),
('kevin','male',81,'20130305','teacher',8300,401,1),
('tank','male',73,'20140701','teacher',3500,401,1),
('owen','male',28,'20121101','teacher',2100,401,1),
('jerry','female',18,'20110211','teacher',9000,401,1),
('nick','male',18,'19000301','teacher',30000,401,1),
('sean','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 * from emp;

技术图片

另一种结果排版 \\G

当表字段特别多的时候,结果的排版可能会出现混乱的现象,你可以在语句最后加 \\G 来改变排版,方便查看

技术图片

比较差的展示结果,这种情况就非常适合用 \\G 来看数据

技术图片

简单查询语句的书写与执行顺序

查询语句书写

查询出 emp 表中id 在 3~6 的员工详细信息

思路:从emp 表中,查 id 大于3 且 小于 6 的数据

技术图片

语句

select * from emp where id > 3 and id < 6;

这里仅为了演示书写顺序,不考虑其他写法

执行顺序

最先执行的是 from,来确定到底是哪张表

然后执行 where,根据条件筛选数据

最后执行 select,来拿筛选出来的数据中的(某些,select 后面跟的字段名)字段

科普-- 起别名

关键字 as

  • 可以给表起别名
  • 可以给查询出来的虚拟表(查询结果)起别名
  • 可以给字段起别名
  • 可以给函数的结果取别名(max、min 等)

写法

要起别名的对象 as 别名 或者 直接 要起别名的对象 别名

不过尽量还是用as ,不用as 可能语义不明确

给函数结果起别名

... max(hire_date) as max_date ...

给表起别名

select ... from emp as t1 ....

给查询出来的虚拟表取别名

... (select * from emp) as t2 ...

给字段起别名

select name as ‘姓名‘, post ‘部门‘ from emp;

技术图片

可以对字段做四则运算(加减乘数)

查一下 jason 的年薪

select name as ‘姓名‘, salary * 12 as ‘年薪‘ from emp where name = ‘jason‘;

技术图片

concat 格式化拼接字段

可以按指定格式拼接字段

select concat('oldboy_', name, '_', id), sex, post, salary from emp;

技术图片

concat_ws 用指定字符拼接字段

select concat_ws(‘:‘, name, sex, age) from emp;

技术图片

定制化查询结果

复杂查询实现小窍门:

写sql语句的时候,千万不要急着一口气写完(切忌心浮气躁)

前期按照歩鄹一步步写将前一步操作产生的结果当成是一张新的表,然后基于该表再进行其他操作,写一步查询看一下结果然后基于当前结果再往后写

我们查询数据一般都需要做一些过滤,单纯靠 select * from 表名; 就无法达到要求,此时我们可以通过

常见的数据定制化关键字(非多表查询)

  • where 条件过滤数据

    一般配合一堆聚合函数使用

  • group by 对数据进行分组

    • having 对分组的结果再进行条件过滤(必须跟在 group by 语句后面)

  • distinct 对查询结果去重

  • order by 对查询结果排序

  • limit 限制显示数据条数

where 结合过滤条件过滤结果

> < = != <= >= <> 比较运算符

# 查询出 emp 表中, id 大于3 的员工信息
select * from emp where id > 3;

技术图片

and or not 与或非连接多个条件

一般用来连接多个条件

and 并且

or 或

not 非

is

针对 null 判断的时候只能用 is 不能用 =

案例

# and
# 1.查询id大于等于3小于等于6的数据
select id,name from emp where id >= 3 and id <= 6;

# or
# 2.查询薪资是20000或者18000或者17000的数据
select * from emp where salary = 20000 or salary = 18000 or salary = 17000;

# not
# 5.查询id小于3或者大于6的数据
select *  from emp where id not between 3 and 6;

# is
# 7.查询岗位描述为空的员工名与岗位名  针对null不能用等号,只能用is
select name,post from emp where post_comment = NULL;  # 查询为空,不能用 = 判断空!
select name,post from emp where post_comment is NULL;
select name,post from emp where post_comment is not NULL;

范围

between ... and ... 表示范围(整型字段可用)

in

and or not 结果其他关键字组合

案例

# between ... and ... 
# 1.查询id大于等于3小于等于6的数据
select *  from emp where id between 3 and 6;

# in
# 2.查询薪资是20000或者18000或者17000的数据
select * from emp where salary in (20000,18000,17000);

# and or not
# 在上一模块中有案例

exists 是否存在

# EXISTS 关键字表示存在
# 返回值是 True 或者 False
select * from emp where exists (select id from dep where id > 203);  # 用到了下面的子查询(知识点)

技术图片

like 模糊匹配

一般包含有 ... 之类的查询都会用 like 关键字,模糊匹配

小技巧

是否含有用 % 包围起来

固定长度用 来占位,一个 _ 表示一个字符

案例

# 3.查询员工姓名中包含o字母的员工姓名和薪资
select name,salary from emp where name like '%o%';

# 4.查询员工姓名是由四个字符组成的员工姓名与其薪资
# 方案一:用四个 _ 代替四个字符
select name,salary from emp where name like '____';
# 方案二:利用 char_length(字段名) 来获取字段长度
select name,salary from emp where char_length(name) = 4;  # 注意 sql_mode 里 PAD_CHAR_TO_FULL_LENGTH 这么个约束,否则 char类型定长可能会受影响

regexp 正则

可用正则规则匹配字符串作为查询条件

select * from emp where name regexp ‘^j.*(n|y)$‘;

技术图片

group by 分组

分组之后应该做到最小单位是组,而不应该再展示组内的单个信息

mysql 中分组之后,只能拿到分组的字段信息无法直接获取其他字段信息

但是你可以通过其他方法(如:聚合函数)间接地获取

分组相当于打包,聚合函数可以对包里每一个元素进行处理,最终拿出想要的

刚开始查询表,一定要按照最基本的步骤,先确定是哪张表,再确定查这张表也没有限制条件,再确定是否需要分类,最后再确定需要什么字段对应的信息

应用场景

每个部门的平均薪资,男女比例等

分组严格模式(推荐开启)

select * from emp group by post; 如果你的MySQL不报错,说明分组的严格模式没有设置

--> only_full_group_by 限制分组

非分组严格模式下

技术图片

设置分组严格模式(其他的严格模式别忘写了)

set global sql_mode=‘strict_trans_tables,pad_char_to_full_length,only_full_group_by‘;

技术图片

pad_char_to_full_length 验证 char varchar占用空间用,会影响 char_length() 获取的长度

strict_trans_tables 限制 sql 不能不合规则的直接报错

分组严格模式下执行,直接报错(day37.emp.id --> 数据库day37.表emp.字段id),字段不在分组里

技术图片

having 分组条件

havingwhere 是一模一样的,也是用来筛选数据的,但是 having 必须在 group by 后面使用

where是对整体数据做一个初步的筛选,而having是对分组之后的数据再进行一次针对性的筛选

select post, avg(salary) from emp where age > 30 group by post having avg(salary) > 10000;

统计各部门年龄在30岁以上的员工平均工资,并且保留平均工资大于10000的部门

技术图片

分组结合聚合函数

只能在分组之后使用(如果没有写group by ,默认所有数据就是一组

也可以说是 where 不能用聚合函数(执行顺序过了 where之后就可也以算分组之后了--> 执行顺序)

max min avg sum count

能够获取到分组之后除了分组依据以外的字段,将该字段作为函数的条件

# 强调:只要分组了,就不能够再“直接”查找到单个数据信息了,只能获取到组名
# 2.获取每个部门的最高工资  
# 以组为单位统计组内数据>>>聚合查询(聚集到一起合成为一个结果)
# 每个部门的最高工资
select post,max(salary) from emp group by post;
# 每个部门的最低工资
select post,min(salary) from emp group by post;
# 每个部门的平均工资
select post,avg(salary) from emp group by post;
# 每个部门的工资总和
select post,sum(salary) from emp group by post;

# 每个部门的人数
# 在统计分组内个数的时候,填写任意非空字段都可以完成计数(推荐使用能够标识数据的字段,比如id字段)
select post,count(id) from emp group by post;

group_concat 分组拼接记录字段

能够获取到分组之后除了分组依据以外的字段,还能做拼接操作

# 3.查询分组之后的部门名称和每个部门下所有的学生姓名
# group_concat(分组之后用)不仅可以用来显示除分组外字段还有拼接字符串的作用
select post,group_concat(name) from emp group by post;

select post,group_concat(name,"_SB") from emp group by post;

select post,group_concat(name,": ",salary) from emp group by post;

select post,group_concat(salary) from emp group by post;


# 4.补充concat(不分组时用)拼接字符串达到更好的显示效果 as语法使用(前面有讲到)
select name as 姓名,salary as 薪资 from emp;
select concat("NAME: ",name) as 姓名,concat("SAL: ",salary) as 薪资 from emp;

/*
concat  在不分组情况下使用
group_concat  用在分组之后
*/

distinct 去重

对整个查询(查询出的虚拟表)结果中重复的数据去重,重复必须数据是一模一样的才能去重,只要有一个(字段)不一样都不能算是重复的数据

如果你查询出来的数据中包含主键(非空且唯一),那么不可能去重成功

个人推荐理解成作用于上一步查询结果的(不要以为像order by一样修饰某个字段)

select distinct age, id from emp;

查询结果有重复的情况下,会自动去除重复

技术图片

select distinct sex, age, id from emp order by sex, age asc;

记录没有重复,distinct 无效

技术图片

order by 排序

order by 有升序(ASC)、降序(DESC)两种排序规则,默认升序

多个排序字段时,放前面的作为优先排序条件,相同再按照后面的字段排序

select post, avg(salary) from emp where age>10 group by post having avg(salary) > 1000 order by avg(salary);

单个字段排序

技术图片

select * from emp order by sex, age asc;

多字段排序

技术图片

limit 限制展示数据的条数

select * from emp limit 3;

当limit 只有一个参数的时候,表示的是从第一条开始只展示几条

技术图片

select * from emp limit 5,5;

当limit 有两个参数的时候,第一个参数表的起始位置,第二个参数表示从起始位置开始往后展示的条数

技术图片

练习

查询工资最高的人的详细信息

select * from emp order by salary desc limit 1;

技术图片

应用场景

分页数据展示,每页只展示多少条,每页展示的内容,是第几条到第几条

究极版执行顺序书写顺序

书写顺序(除了 select ... from ... 其他是可选的)

select distinct 字段1,字段2(有分组时只能写分组字段或聚合函数) from 表名
where 条件(不能用聚合函数)
group by (单个)字段
having 条件
order by 字段1,字段2 排序规则
limit 起始位置,条数;

执行顺序

from
where
group by
    having  # 必须跟在 group by 后面
# 后4个顺序不太重要,也不一定对
order by
limit
distinct
select

多表查询

前言

在昨天的知识点中,员工信息全存为一张表不太合理,我们选择了拆表,分析了表关系,最终拆分成了员工表与部门表两张表,表示拆分好了,但怎么去查询数据呢?

实现多表查询,有下面两种方式

  • 联表查询
  • 子查询

每一次的查询结果都是一张虚拟表,我们可以用 as 关键字给虚拟表取别名,然后将其当做普通表作为查询条件使用

测试数据创建

创建数据库与表

create database db2;
use db2;

create table dep(
id int,
name varchar(20) 
);

create table emp(
id int primary key auto_increment,
name varchar(20),
sex enum('male','female') not null default 'male',
age int,
dep_id int
);

插入表记录数据

insert into dep values
(200,'技术'),
(201,'人力资源'),
(202,'销售'),
(203,'运营');

insert into emp(name,sex,age,dep_id) values
('jason','male',18,200),
('egon','female',48,201),
('kevin','male',38,201),
('nick','female',28,202),
('owen','male',18,200),
('jerry','female',18,204);

# 当初为什么我们要分表,就是为了方便管理,在硬盘上确实是多张表,但是到了内存中我们应该把他们再拼成一张表进行查询才合理

笛卡尔集/积 -- 科普

笛卡尔集的列数为每个表的列数之和,笛卡尔集的行数为每个表的行数相乘

我们经常做的多表查询就是在笛卡尔集中通过筛选条件得出的数据,所以笛卡尔集是多表查询的基础

select * from emp, dep; 结果是一个笛卡尔集/积

技术图片

select * from emp;

技术图片

select * from dep;

技术图片

后面跟条件也可以达到多表查询

select * from emp, dep where emp.dep_id = dep.id

技术图片

联表查询

通过下面四种连接语句来实现多表查询

inner/left/right join ...左右是表 on ... 后面可以跟条件

内连接 inner join ... on

仅保留两张表有对应关系的记录

select * from emp inner join dep on emp.dep_id=dep.id;

技术图片

左连接 left join ... on

在内连接的基础上保留左表没有对应关系的记录

select * from emp left join dep on emp.dep_id = dep.id;

技术图片

右连接 right join ... on

在内连接的基础上保留右表没有对应关系的记录

select * from emp right join dep on emp.dep_id = dep.id;

技术图片

全连接 union ... on

不常用

在内连接的基础上保留左、右面表没有对应关系的的记录

写法:只需要在左连接和右连接的sql 语句中间加个union就变成了全连接

select * from emp left join dep on emp.dep_id = dep.id  # 左连接 sql,后面不要加分号
union
select * from emp right join dep on emp.dep_id = dep.id;  # 右连接 sql

技术图片

子查询

将一个查询语句用括号括起来,将查询结果(虚拟表)作为另外一个 sql 语句的查询条件

ps:表的查询结果可以作为其他表的查询条件,也可以通过起别名的方式把它作为一张虚拟表跟其他表做关联查询

# 1.查询部门是技术或者人力资源的员工信息

# 思路
# 先获取技术部和人力资源部的id号,再去员工表里面根据前面的id筛选出符合要求的员工信息
select * from emp where dep_id in (select id from dep where name = "技术" or name = "人力资源");

技术图片

select * from emp;

技术图片

select id from dep where name = "技术" or name = "人力资源";

技术图片

# 2.每个部门最新入职的员工

# 思路
# 先查每个部门最新入职的员工,再按部门对应上联表查询

select t1.id, t1.name, t1.hire_date, t1.post from emp as t1
inner join
# 根据分组求出最新入职员工
(select post,max(hire_date) as max_date from emp group by post) as t2
on t1.post = t2.post
where t1.hire_date = t2.max_date;

技术图片

完整表信息

技术图片

练习小案例

# 平均年龄在25岁以上的部门名

# 联表
select dep.name from dep inner join emp on emp.dep_id = dep.id group by dep.name having avg(age) > 25;

# 子查询
select name from dep where dep.id in (select dep_id from emp group by dep_id having avg(age) > 25);

联表查询思路

1.先把两张表连起来,把结果查出来看看,再接着往下写

select * from dep inner join emp on emp.dep_id = dep.id;

技术图片

2.再根据部门分组,筛选出平均年龄大于25的部门名

group by dep.name having avg(age) > 25

合并上一步的语句(* --> dep.name)

select dep.name from dep inner join emp on emp.dep_id = dep.id group by dep.name having avg(age) > 25;

技术图片

子查询思路

1.先根据 部门id 分组,查出平均年龄大于 25多的部门id

select dep_id from emp group by dep_id having avg(age) > 25;

技术图片

2.将上一步的 部门id 作为筛选条件 联合上部门表查出来

select name from dep where dep.id in (select dep_id from emp group by dep_id having avg(age) > 25);

技术图片

以上是关于MySQL-复杂查询及条件-起别名-多表查询-04的主要内容,如果未能解决你的问题,请参考以下文章

MySQL连接查询

mysql 多表联合查询啥用

Day04 多表查询

sql mysql多表如何关联查询

Mysql数据库操作总结

mysql多表查询之子语句查询