Uploaded by Elaine Huang

SQL(猴子的数据分析)

advertisement
如何找出重复的值
如何查找第N⾼高的数值
如何实现多表查询
查找不不在表中的数据
【题⽬目1】:如何找出重复的值
编写⼀一个SQL查询,查找学⽣生表中所有重复的学⽣生名。
解题思路路】
1.看到“找重复”的关键字眼,⾸首先要⽤用分组函数(group by),再
⽤用聚合函数中的计数函数count()给姓名列列计数。
2. 分组汇总后,⽣生成了了⼀一个如下的表。从这个表⾥里里选出计数⼤大于1
的姓名,就是重复的姓名。
⽅方法1:
1)创建⼀一个辅助表,将姓名列列进⾏行行⾏行行分组汇总
select 姓名, count (姓名)as 计数
from 学⽣生表
group by 姓名
2)选出辅助表中计数⼤大于1的姓名
select 姓名
from 辅助表
where 计数 >1
3)结合前两步,将“创建辅助表”的步骤放⼊入⼦子查询
select 姓名
from (
select 姓名, count (姓名)as 计数
from 学⽣生表
group by 姓名
) as 辅助表
where 计数 >1
最优⽅方法:
select 姓名
from 学⽣生表
group by 姓名
having count(姓名)>1
题⽬目⼆二:如何查找第N⾼高的数据
【题⽬目】
现在有“课程表”,记录了了学⽣生选修课程的名称以及成绩。
现在需要找出语⽂文课中成绩第⼆二⾼高的学⽣生成绩。如果不不存在第⼆二⾼高
成绩的学⽣生,那么查询应返回 null。
1.找出所有选修了了“语⽂文”课的学⽣生成绩
select *
from 课程表
where 课程= ‘语⽂文ʼ
2.查找语⽂文课程成绩的第⼆二名
考虑到成绩可能有⼀一样的值,所以使⽤用distinct 成绩进⾏行行成绩去
重。
思路路1:
使⽤用⼦子查询找出语⽂文成绩查询最⼤大的成绩记为a,然后再找出⼩小于
a的最⼤大值就是课程成绩的第⼆二⾼高值。
max(列列名) 可以返回该列列的最⼤大值
可以⽤用下⾯面的sql语句句得到语⽂文课的最⼤大值
select max(distinct(成绩)
from 课程表
where 课程= ‘语⽂文ʼ
然后再找出⼩小于a的最⼤大值就是课程成绩的第⼆二⾼高值。
select max(distinct(成绩)
from 课程表
where 课程= ‘语⽂文ʼ and
成绩 < (select max(distinct(成绩)
from 课程表
where 课程= ‘语⽂文ʼ)
思路路2:使⽤用 limit 和 offset
在《猴⼦子 从零学会sql》中讲过:
limit n⼦子句句表示查询结果返回前n条数据
offset n表示跳过x条语句句
limit y offset x 分句句表示查询结果跳过 x 条数据,读取前 y 条数据
使⽤用limit和offset,降序排列列再返回第⼆二条记录可以得到第⼆二⼤大的
值
select distinct 成绩
from 成绩表
wheree 课程=‘语⽂文ʼ
order by 课程,成绩 desc
limit 1,1 (跳过⼀一条,读取第⼀一条)
3.考虑特殊情况
题⽬目要求,如果没有第⼆二⾼高的成绩,返回空值,所以这⾥里里⽤用判断空
值的函数(ifnull)函数来处理理特殊情况。
ifnull(a,b)函数解释:
如果value1不不是空,结果返回a
如果value1是空,结果返回b
对于本题的sql就是:
select ifnull(
(select max(distinct(成绩)
from 课程表
where 课程= ‘语⽂文ʼ and
成绩 < (select max(distinct(成绩)
from 课程表
where 课程= ‘语⽂文ʼ)
,null) as ‘语⽂文课第⼆二名成绩ʼ
【举⼀一反三】
查找 Employee 表中第⼆二⾼高的薪⽔水(Salary)。查询结果返回 200
作为第⼆二⾼高的薪⽔水。如果不不存在第⼆二⾼高的薪⽔水,那么查询应返回
null。
select ifnull(
select distinct Salary
from Employee
order by Salary Desc
limit 1,1), null
) as secondhighsalary
【题⽬目3】 多表联合查询
【题⽬目】
现在有两个表,“学⽣表”记录了学⽣的基本信息,有“学号”、“姓
名”。
“成绩”表记录了学⽣选修的课程,以及对应课程的成绩。
这两个表通过“学号”进⾏关联。
现在要查找出所有学⽣的学号,姓名,课程和成绩。
select a.学号,a.课程,b.课程,b.成绩
from 学⽣生 as a
left join 成绩 as b
on a.学号=b.学号
【举⼀一反三】
有下⾯面两个表
select a.firstName, a.LastName, b.City,b.state
from Person as a
Left join address as b
on a.personid= b.personid
【题⽬目4】查找不不在表中的数据
下⾯面是学⽣生的名单,表名为“学⽣生表”;近视学⽣生的名单,表名
为“近视学⽣生表”。请问不不是近视眼的学⽣生都有谁?
(“学⽣生表”表中的学号与“近视学⽣生”表中的学⽣生学号⼀一⼀一对应)
select a.姓名 as 不不近视的学⽣生名单
from 学⽣生表 as a
left join 近视学⽣生表 as b
on a.学号=b.学⽣生学号
where b.序号 is null;
举⼀一反三】
查找“不不在表⾥里里的数据”应⽤用案例例:
某⽹网站包含两个表,顾客姓名表(表名Customers)和 购买记录
表(表名Orders)。找出所有从不不订购任何东⻄西的客户。
(“顾客姓名表”中的ID与“购买记录”表中的学⽣生学号CustomerId⼀一
⼀一对应)
select a.names as Customers
from Customers as a
left join Orders as b
on a.ID =b.customerId
where b.customerid is null
【题⽬目5】
“雇员表“中记录了了员⼯工的信息,“薪⽔水表“中记录了了对应员⼯工发放的
薪⽔水。两表通过“雇员编号”关联。
查找当前所有雇员⼊入职以来的薪⽔水涨幅,给出雇员编号以及其对应
的薪⽔水涨幅,并按照薪⽔水涨幅进⾏行行升序。
(注:薪⽔水表中结束⽇日期为2004-01-01的才是当前员⼯工,否则是
已离职员⼯工)
select m.雇员编号,当前薪⽔水-⼊入职薪⽔水 as 薪⽔水涨幅
from
(select 雇员编号,薪⽔水 as 当前薪⽔水
from 薪⽔水表
where 结束⽇日期 = '2004-01-01') as m
left join
(select a.雇员编号,薪⽔水 as ⼊入职薪⽔水
from 雇员表 as a
left join 薪⽔水表 as b
on a.雇员编号 = b.雇员编号
where a.雇⽤用⽇日期 = b.起始⽇日期 and a.雇员编号 in
(select 雇员编号
from 薪⽔水表
where 结束⽇日期 = '2004-01-01')) as n
on m.雇员编号 = n.雇员编号
order by 薪⽔水涨幅;
【举⼀一反三】
查找所有学⽣生开学以来的成绩涨幅,给出学⽣生编号以及其对应的成
绩涨幅,并按照成绩涨幅进⾏行行升序。
select m.学⽣生编号,当前成绩-⼊入学成绩 as 成绩涨幅
from
(select 学⽣生编号,成绩 as 当前成绩
from 成绩表
where 结束⽇日期 = '2011-10-02') as m
left join
(select a.学⽣生编号,b.成绩 as ⼊入学成绩
from 学⽣生表 as a
left join 成绩表 as b
on a.学⽣生编号 = b.学⽣生编号
where a.⼊入学⽇日期 = b.起始⽇日期) as n
on m.学⽣生编号 = n.学⽣生编号
order by 成绩涨幅;
【题⽬目6】如何⽐比较⽇日期数据
下⾯面是某公司每天的营业额,表名为“⽇日销”。“⽇日期”这⼀一列列的数据
类型是⽇日期类型(date)。
请找出所有⽐比前⼀一天(昨天)营业额更更⾼高的数据。(前⼀一天的意
思,如果“当天”是1⽉月,“昨天”(前⼀一天)就是1号)
例例如需要返回⼀一下结果:
【解题思路路】
1.交叉联结
使⽤用交叉联结会将两个表中所有的数据两两组合。如下图,是对
表“text”⾃自身进⾏行行交叉联结的结果:
直接使⽤用交叉联结的业务需求⽐比较少⻅见,往往需要结合具体条件,
对数据进⾏行行有⽬目的的提取,本题需要结合的条件就是“前⼀一天”。
2.本题的⽇日销表交叉联结的结果(部分)如下。这个交叉联结的结
果表,可以看作左边三列列是表a,右边三列列是表b。
红⾊色框中的每⼀一⾏行行数据,左边是“当天”数据,右边是“前⼀一天”的数
据。⽐比如第⼀一个红⾊色框中左边是“当天”数据(2号),右边是“前⼀一
天”的数据(1号)。
题⽬目要求,销售额条件是:“当天” > “昨天”(前⼀一天)。所以,对
于上⾯面的表,我们只需要找到表a中销售额(当天)⼤大于b中销售
额(昨天)的数据。
3.另⼀一个需要着重去考虑的,就是如何找到 “昨天”(前⼀一天),这
⾥里里为⼤大家介绍两个时间计算的函数:
datediff(⽇日期1, ⽇日期2):
得到的结果是⽇日期1与⽇日期2相差的天数。
如果⽇日期1⽐比⽇日期2⼤大,结果为正;如果⽇日期1⽐比⽇日期2⼩小,结果为负。
例例如:⽇日期1(2019-01-02),⽇日期2(2019-01-01),两个⽇日期
在函数⾥里里互换位置,就是下⾯面的结果
另⼀一个关于时间计算的函数是:
timestampdiff(时间类型, ⽇日期1, ⽇日期2)
这个函数和上⾯面diffdate的正、负号规则刚好相反。
⽇日期1⼤大于⽇日期2,结果为负,⽇日期1⼩小于⽇日期2,结果为正。
在“时间类型”的参数位置,通过添加“day”, “hour”, “second”等关
键词,来规定计算天数差、⼩小时数差、还是分钟数差。示例例如下
图:
【解题步骤】
1.将⽇日销表进⾏行行交叉联结
2.选出上图红框中的“a.⽇日期⽐比b.⽇日期⼤大⼀一天”
可以使⽤用“diffdate(a.⽇日期, b.⽇日期) = 1”或者“timestampdiff(day, a.
⽇日期, b.⽇日期) = -1”,以此为基准,提取表中的数据,这⾥里里先⽤用
diffdate进⾏行行操作。
3.找出a中销售额⼤大于b中销售额的数据
where a.销售额(万元) > b.销售额(万元)
4.删掉多余数据
题⽬目只需要找销售额⼤大于前⼀一天的ID、⽇日期、销售额,不不需要上表
那么多数据。所以只需要提取中上表的ID、⽇日期、销售额(万元)
列列。
select a.ID, a.⽇日期, a.销售额(万元)
from ⽇日销 as a cross join ⽇日销 as b
on datediff(a.⽇日期, b.⽇日期) = 1
where a.销售额(万元) > b.销售额(万元);
或者
select a.ID, a.⽇日期, a.销售额(万元)
from ⽇日销 as a cross join ⽇日销 as b
on timestampdiff(day, a.⽇日期, b.⽇日期) = -1
where a.销售额(万元) > b.销售额(万元);
【举⼀一反三】
下⾯面是⽓气温表,名为weather,date列列的数据格式为date,请找出
⽐比前⼀一天温度更更⾼高的ID和⽇日期
select a.ID, a.date
from weather as a
cross join weather as b
on datediff(a.date,b.date)=1
where a.temp >b.temp
【题⽬目7】交换数据
⼩小明是⼀一所学校的⽼老老师,她有⼀一张 ‘学⽣生表ʼ,平时⽤用来存放座位号
和学⽣生的信息。其中,座位号是连续递增的。总的座位数是偶数。
现在,⼩小明想改变相邻俩学⽣生的座位。你能不不能帮她写⼀一个sql查
来输出想要的结果呢?
示例例查询结果如下:
【解题思路路】
第⼀一步:理理清换座位的逻辑
查询⽬目的是改变相邻学⽣生的座位号。为了了理理清逻辑,在原表中插⼊入
⼀一列列叫做‘奇偶数ʼ,对应表示“座位号”的值是“奇数”还是“偶数”。
然后⽐比较原始表⾥里里的“座位号”和交换结果⾥里里的“座位号”(对⽐比分析
⽅方法),可以发现下图的规律律。
1)如果原来座位号是奇数的学⽣生,换座位后,这名学⽣生的座位号
变为“座位号+1”。
2)如果原来座位号是偶数的学⽣生,换座位后,这名学⽣生的座位号
变为“座位号-1”。
第⼆二步:如何判断座位号是奇数,还是偶数
sql求余函数:mod(n,m) ,返回n除以m的余数。⽐比如mod(8,2) 的结
果是0。
如果n除以2的余数是0,说明n是偶数,否则是奇数。
转换为判断奇数,偶数的sql就是:
把前⾯面的逻辑写到sql⾥里里就是:
1)如果原来座位号是奇数的学⽣生,换座位后,这名学⽣生的座位号
变为“座位号+1”。
2)如果原来座位号是偶数的学⽣生,换座位后,这名学⽣生的座位号
变为“座位号-1”。
加⼊入select字句句,就是最好的结果:
select
(case
when mod(座位号, 2) != 0 then 座位号 + 1
when mod(座位号, 2) = 0 then 座位号 - 1
end) as '交换后座位号',
姓名
from 学⽣生表;
【举⼀一反三】
原始座次表 ‘seatʼ如下,现需要更更换相邻位置学⽣生的座次。
注:该座次表‘seatʼ共有5名学⽣生,第5个 座位号是奇数的不不变
select
(case
# 当座位号是奇数并且不不是不不是最后⼀一个座位号时
when mod(id, 2) != 0 and counts!= id then id + 1
# 当座位号是奇数并且是最后⼀一个座位号时,座位号不不变
when mod(id, 2) != 0 and counts = id then id
# 当座位号是偶数时
else id - 1
end) as id2,student
from seat as a,
(select count(*) as counts from seat) as b;
Download