mysql查询
单表查询
"""
增:
insert [into]
[数据库名.]表名[(字段1[,...,字段n])]
values
(数据1[,...,数据n])[,...,(数据1[,...,数据n])];
eg:
增:
insert into db1.student(id, name, age) values(1, \'nick\', 18);
删:
delete from db1.student where id = 1;
改:
update db1.student set name = \'tank\' where id = 1;
"""
#条件:from, where, group by, having, distinct, order by, limit
#==> 层层筛选后的结果
#注:一条查询语句,可以拥有多种筛选条件,条件的顺序必须按照上方的顺序进行逐步筛选,distinct稍有特殊(书写位置),条件的种类可以不全
#可以缺失,但不能乱序
去重:distinct
create table t1(
id int,
x int,
y int
);
insert into t1 values(1, 1, 1), (2, 1, 2), (3, 2, 2), (4, 2, 2);
select distinct * from t1;
#id为3和4的两列不会去重,按*取值就是这样,即全部数据,但是如果又插入了一个(4,2,2),这样就有5行数据了,执行完语句就只剩了4行,即全部(id,x,y)都一样,才会删掉。
select distinct x, y from t1;
#id为3和4的两列重复了,所以会去掉一组,结果是1,1 1,2 2,2
select distinct y from t1;
#只筛选y这列,y中的2重复了,结果是1 2
数据准备
CREATE TABLE `emp` (
`id` int(0) NOT NULL AUTO_INCREMENT,
`name` varchar(10) NOT NULL,
`gender` enum(\'男\',\'女\',\'未知\') NULL DEFAULT \'未知\',
`age` int(0) NULL DEFAULT 0,
`salary` float NULL DEFAULT 0,
`area` varchar(20) NULL DEFAULT \'中国\',
`port` varchar(20) DEFAULT \'未知\',
`dep` varchar(20),
PRIMARY KEY (`id`)
);
INSERT INTO `emp` VALUES
(1, \'yangsir\', \'男\', 42, 10.5, \'上海\', \'浦东\', \'市场部\'),
(2, \'engon\', \'男\', 38, 9.4, \'山东\', \'济南\', \'研发部\'),
(3, \'jerry\', \'女\', 30, 3.0, \'江苏\', \'张家港\', \'研发部\'),
(4, \'tank\', \'女\', 28, 2.4, \'广州\', \'广东\', \'研发部\'),
(5, \'jiboy\', \'男\', 28, 2.4, \'江苏\', \'苏州\', \'研发部\'),
(6, \'zero\', \'男\', 18, 8.8, \'中国\', \'黄浦\', \'销售部\'),
(7, \'owen\', \'男\', 18, 8.8, \'安徽\', \'宣城\', \'研发部\'),
(8, \'jason\', \'男\', 28, 9.8, \'安徽\', \'巢湖\', \'研发部\'),
(9, \'ying\', \'女\', 36, 1.2, \'安徽\', \'芜湖\', \'销售部\'),
(10, \'kevin\', \'男\', 36, 5.8, \'山东\', \'济南\', \'研发部\'),
(11, \'monkey\', \'女\', 28, 1.2, \'山东\', \'青岛\', \'市场部\'),
(12, \'san\', \'男\', 30, 9.0, \'上海\', \'浦东\', \'销售部\'),
(13, \'san1\', \'男\', 30, 6.0, \'上海\', \'浦东\', \'销售部\'),
(14, \'san2\', \'男\', 30, 6.0, \'上海\', \'浦西\', \'研发部\'),
(15, \'ruakei\', \'女\', 67, 2.501, \'上海\', \'陆家嘴\', \'研发部\');
常用函数
"""
拼接:concat() | concat_ws()
大小写:upper() | lower()
浮点型操作:ceil() | floor() | round()
整型:可以直接运算
"""
select concat(name,\':\',area,\'-\',port) 个人信息 from emp;
#concat用法,直接把填入的符号进行拼接
#结果是一张只有一列的表,标题是‘个人信息’,内容是‘yangsir:上海-浦东,....,ruakei: 上海-陆家嘴\'
select upper(name) 姓名大写,lower(name) 姓名小写 from emp;
#两列,左侧标题是’姓名大写‘列是大写,右侧标题是’姓名小写‘列是小写。
select id,salary,ceil(salary)上薪资,floor(salary)下薪资,round(salary)入薪资 from emp;
#ceil向上取整,floor向下取整
#这里的四舍五入会出现错误,比如round(10.5)得到结果是10,但是round(10.501)得到的结果就是11
select id,salary 旧薪资,salary+1 新薪资 from emp;
#新工资这一列会保留很多小数
条件:where
#多条件协调操作导入: where 奇数 [group by 部门 having 平均薪资] order by [平均]薪资 limit 1
mysql>: select * from emp where id<5 limit 1; # 正常
mysql>: select * from emp limit 1 where id<5; # 异常,条件乱序
#正确的顺序是(from where group by having distinct order by limit)
# 判断规则
"""
比较符合:> | < | >= | <= | = | !=
区间符合:between 开始 and 结束 | in(自定义容器)
逻辑符合:and | or | not
相似符合:like _|%
正则符合:regexp 正则语法
"""
mysql>: select * from emp where salary>5; #工资大于5的的信息
mysql>: select * from emp where id%2=0; #id为偶数的信息
mysql>: select * from emp where salary between 6 and 9; #工资在6和9之间(包括 6和9 )
mysql>: select * from emp where id in(1, 3, 7, 20); #id包含括号里面要求的
mysql>: select * from emp where name like \'%o%\';
#模糊查询 %表示所有字符,占任意位,所以这句话的意思是name里面包含字母o的记录
mysql>: select * from emp where name like \'_o%\';
#_占一个位,home满足上面式子的表达
# sql只支持部分正则语法
mysql>: select * from emp where name regexp \'.*\d\'; # 不支持\d代表数字,认为\d就是普通字符串
mysql>: select * from emp where name regexp \'.*[0-9]\'; # 支持[]语法
mysql>:select * from emp where not name(或者是name not) regexp \'.*[0-9]\'
#上面这句话查找的是名字最后一个不是数字的所有名字
分组与筛选:group by | having
where与having
# 表象:在没有分组的情况下,where与having结果相同
# 重点:having可以对 聚和结果 进行筛选
mysql>: select * from emp where salary > 5;
mysql>: select * from emp having salary > 5;
mysql>: select * from emp where id in (5, 10, 15, 20);
mysql>: select * from emp having id in (5, 10, 15, 20);
聚合函数
"""
聚合函数不管在分没分组的情况下都可以使用。
max():最大值
min():最小值
avg():平均值
sum():和
count():记数
group_concat():组内字段拼接,用来查看组内其他字段
"""
分组查询 group by
# 修改my.ini配置重启mysql服务
sql_mode=ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
# 在sql_mode没有 ONLY_FULL_GROUP_BY 限制下,可以执行,但结果没有意义(比如部门有3个销售部,两个研发部,那这种情况下执行后的结果是按顺序查找一个销售和一个研发,一共两条字段)
# 有 ONLY_FULL_GROUP_BY 限制,报错
mysql>: select * from emp group by dep;
#分组的意思就是,按照分组的条件,按照顺序找到不同分组条件的字段
#比如上面这句话的意思是按照dep进行分组,则结果就是在emp中,自上而下找到不同的dep字段然后展示出来(比如找到第一个研发部,则第二个研发部就不展示了)
#如果想把每个部门的员工展示出来,用以下语句
#select dep 部门, group_concat(name) 员工们 from emp group by dep;
# 分组后,表中数据考虑范围就不是 单条记录,因为每个分组都包含了多条记录,参照分组字段,对每个分组中的 多条记录 统一处理(比如按部门分组,那么每个部门都包含了多个字段)
# eg: 按部门分组,每个部门都有哪些人、最高的薪资、最低的薪资、平均薪资、组里一共有多少人
# 将多条数据统一处理,这种方式就叫 聚和
# 每个部门都有哪些人、最高的薪资、最低的薪资、平均薪资 都称之为 聚和结果 - 聚和函数操作的结果
# 注:参与分组的字段,也归于 聚和结果
mysql>:
select
dep 部门,
group_concat(name) 成员,
max(salary) 最高薪资,
min(salary) 最低薪资,
avg(salary) 平均薪资,
sum(salary) 总薪资,
count(gender) 人数
from emp group by dep;
# 先从emp里面按照dep进行分组,然后把列出的字段表示出来。
# 总结:分组后,查询条件只能为 分组字段(即按照dep分组后的字段) 和 聚和函数(即select中选择的函数)操作的聚和结果
分组后的having
# 最低薪资小于2
mysql>:
select
dep 部门,
group_concat(name) 成员,
max(salary) 最高薪资,
min(salary) 最低薪资,
avg(salary) 平均薪资,
count(gender) 人数
from emp group by dep having min(salary)<2;
#其实就是对上一步骤的再一次操作 把最低工资小于2的字段筛选出来,用到having
# having可以对 聚和结果 再进行筛选,where不可以
排序
排序规则
# order by 主排序字段 [asc|desc], 次排序字段1 [asc|desc], ...次排序字段n [asc|desc]
未分组状态下
mysql>: select * from emp;
# 按年龄升序
mysql>: select * from emp order by age asc;
# 按薪资降序
mysql>: select * from emp order by salary desc;
# 按薪资降序,如果相同,再按年龄降序
mysql>: select * from emp order by salary desc, age desc;
# 按龄降序,如果相同,再按薪资降序
mysql>: select * from emp order by age desc, salary desc;
分组状态下
# 最高薪资降序
mysql:
select
dep 部门,
group_concat(name) 成员,
max(salary) 最高薪资,
min(salary) 最低薪资,
avg(salary) 平均薪资,
sum(salary) 总薪资,
count(gender) 人数
from emp group by dep
order by 最高薪资 desc;
#分组状态下的语句是,先分组语句,在排序语句
#上面这句的意思是,先从emp中取值按dep进行分组,然后按照按select选择的字段进行展示,其中最高薪资是降序排列的。
限制 limit
# 语法:limit 条数 | limit 偏移量,条数
mysql>: select name, salary from emp where salary<8 order by salary desc limit 1;
mysql>: select * from emp limit 5,3; # 先偏移5条满足条件的记录,再查询3条(即当前查询表的数据是1-10,那么limit 5,3这句话的意思是从6开始到8)
多表查询
连接
# 连接:将有联系的多张表通过关联(有联系就行,不一定是外键,也可以是name, age 只要这些表中存在相同的字段就可以)字段,进行连接,形成一张大表
# 连表查询:在大表的基础上进行查询,就称之为连表查询
# 将表与表建立连接的方式有四种:内连接、左连接、右连接、全连接
一对多数据准备
mysql>: create database db3;
mysql>: use db3;
#就是部门(一)和员工表(多),
mysql>:
create table dep(
id int primary key auto_increment,
name varchar(16),
work varchar(16)
);
create table emp(
id int primary key auto_increment,
name varchar(16),
salary float,
dep_id int
);
insert into dep values(1, \'市场部\', \'销售\'), (2, \'研发部\', \'开发\'), (3, \'管理部\', \'开车\');
insert into emp(name, salary, dep_id) values(\'egon\', 3.0, 2),(\'yanghuhu\', 2.0, 2),(\'sanjiang\', 10.0, 1),(\'owen\', 88888.0, 2),(\'liujie\', 8.0, 1),(\'yingjie\', 1.2, 0);
笛卡尔积
# 笛卡尔积: 集和 X{a, b} * Y{o, p, q} => Z{{a, o}, {a, p}, {a, q}, {b, o}, {b, p}, {b, q}}
mysql>: select * from emp, dep;
# 总结:是两张表 记录的所有排列组和,数据没有利用价值
内连接
# 关键字:inner join on
# 语法:from A表 inner join B表 on A表.关联字段=B表.关联字段
mysql>:
select
emp.id,emp.name,salary,dep.name,work
from emp inner join dep on emp.dep_id = dep.id
order by emp.id;
#先看from后面的语句,就是说把emp这张表放到dep的左边,他们之间使用emp.dep_id和dep.id进行连接的,连接后的大表是按emp.id进行排列的,然后在按照select的内容进行展示
# 总结:只保留两个表有关联的数据,如果某个表示空值,那么内连接之后就不会保存进去。
左连接
# 关键字:left join on
# 语法:from 左表 left join 右表 on 左表.关联字段=右表.关联字段
mysql>:
select
emp.id,emp.name,salary,dep.name,work
from emp left join dep on emp.dep_id = dep.id
order by emp.id;
#emp所有数据都保留在连接后的左侧,右侧没有与之对应的数据就用0进行填充。
右连接
# 关键字:right join on
# 语法:from A表 right join B表 on A表.关联字段=B表关联字段
mysql>:
select
emp.id,emp.name,salary,dep.name,work
from emp right join dep on emp.dep_id = dep.id
order by emp.id;
# dep所有数据都保留在连接后的右侧,左侧没有与之对应的数据就用0进行填充。
左右可以相互转化
mysql>:
select
emp.id,emp.name,salary,dep.name,work
from emp right join dep on emp.dep_id = dep.id
order by emp.id;
mysql>:
select
emp.id,emp.name,salary,dep.name,work
from dep left join emp on emp.dep_id = dep.id
order by emp.id;
# 总结:更换一下左右表的位置,相对应更换左右连接关键字,结果相同
全连接
mysql>:
select
emp.id,emp.name,salary,dep.name,work
from emp left join dep on emp.dep_id = dep.id
union
select
emp.id,emp.name,salary,dep.name,work
from emp right join dep on emp.dep_id = dep.id
order by id;
# 总结:左表右表数据都被保留,彼此有对应关系正常显示,彼此没有对应关系均空填充对方
一对一与一对多情况一致
#创建一个author表,
create table author(
id int,
name varchar(64),
detail_id int
);
#创建一个作者详情表
create table author_detail(
id int,
phone varchar(11)
);
#插入数据
insert into author values(1, \'Bob\', 1), (2, \'Tom\', 2), (3, \'ruakei\', 0);
insert into author_detail values(1, \'13344556677\'), (2, \'14466779988\'), (3, \'12344332255\');
#把两张表内连接起来,按作者的id号进行记录
select author.id,name,phone from author join author_detail on author.detail_id = author_detail.id order by author.id;
#把作者表全连接作者详情起来,按作者的id号进行记录
select author.id,name,phone from author left join author_detail on author.detail_id = author_detail.id
union
select author.id,name,phone from author right join author_detail on author.detail_id = author_detail.id
order by id;
#全连接,author表在左,如果没有对应的数据就用 0 填充。
多对多:两表两表建立连接
# 在一对一基础上,建立 作者与书 的连接
#先创建作者表
create table author(
id int,
name varchar(64),
detail_id int
);
#插入数据
insert into author values(1, \'Bob\', 1), (2, \'Tom\', 2), (3, \'ruakei\', 0);
#然后创建书表
create table book(
id int,
name varchar(64),
price decimal(5,2)
);
#插入数据
insert into book values(1, \'python\', 3.66), (2, \'Linux\', 2.66), (3, \'Go\', 4.66);
#因为是多对多,所以要设置第三张关系表
create table author_book(
id int,
author_id int,
book_id int
);
# 数据:author-book:1-1,2 2-2,3 3-1,3
insert into author_book values(1,1,1),(2,1,2),(3,2,2),(4,2,3),(5,3,1),(6,3,3);
# 将有关联的表一一建立连接,查询所以自己所需字段
select book.name, book.price, author.name, author_detail.phone from book join author_book on book.id = author_book.book_id
join author on author_book.author_id = author.id
left join author_detail on author.detail_id = author_detail.id;
#先是关系表和书本表连接,再和作者连接,最后再和作者详情连接,其实就是把这几张表全部都连接了,然后选择相关信息查询
小练习
### 基于上面的单表
#1.查询研发部山东人的平均薪资
#分析:查谁,从哪查,条件是啥?(查平均薪资,从研发部,地区是山东的地方查)
select avg(salary) from emp where area=\'山东\' group by dep having dep = \'研发部\'
select avg(salary) from emp where dep=\'研发部\' and area=\'山东\';
select avg(salary) from emp where dep=\'研发部\' group by area having area=\'山东\';
#2.查询姓名中包含英文字母n并且居住在上海的人的所有信息
# 查所有信息,从名字条件和居住地在上海的条件里面查
select * from emp where name like \'%n%\' and area=\'上海\';
#3.查询姓名中包含英文字母n但不包含数字的人的所有信息
#查所有信息,从名字包含英文字母n但是不包含数字的人里面查,用到正则
select * from emp where name like \'%n\' and not name regexp \'.*[0-9]\';
#4.查看各部门的平均年龄并升序排序
#从emp中查平均年龄,按部门分组,按年龄升序
select avg(age) from emp group by dep order by avg(age) desc;
#5.查询各部门中年纪最大的人的姓名与居住地(户籍+区域)
#查的是姓名和居住地(需要用到拼接),按部门分组,要先从部门里面查找年纪最大的信息,返回部门名和最大年纪
select name 姓名, concat_ws(\'-\', area, port) 居住地 from emp
where (age, dep) in (select max(age), dep from emp group by dep);
#6.查询不同年龄层次平均薪资大于5w组中工资最高者的姓名与薪资
# 先查询不同年龄层次平均薪资大于5w的组中的工资最高者,然后再从中选出姓名和薪资
select name,salary from emp where (age,salary) in (select age,max(salary) from emp group by age having avg(salary)>5);
#注意,上面的where(salary,age) 必须和 in中select的(max(salary),age)顺序对应,如果前面是salary,age,后面是max(salary),age,那么就会找到空
#7.查询每一个部门下的员工们及员工职责
# 1、查谁 2、从哪查,信息量不够连表 3、条件是啥,如果存在分组(不能直接查询的字段聚合处理 | 将要查询的字段添加进分组)
# 查的是员工们和员工职责,显然,员工表里面没有职责,所以就要进行分组,把员工的名字做组内拼接,
select work 员工职责, group_concat(emp.name) 员工名 from dep left join emp on dep.id = emp.dep_id by emp.dep_id, work.
#员工和部门是多对一的关系,如果员工们左连接,就会以员工表为准,这样就会有一个职责没有对应,而有个员工没有职责,显然不对,所以是右连接,保证每个职责与员工都会对应,
#通过id进行连接,然后按照员工id(多的一方) 进行排序,max的意思需要唯一确定一个,因为部门数据会重复,然后需要一个组合连接用来表示员工们